ทำไมต้อง Haskell?
(gtf.io)-
"ไม่ค่อยใช้งานจริง", "เชิงวิชาการ", "เฉพาะกลุ่ม"
- นี่คือปฏิกิริยาที่ผู้คนมักแสดงออกเมื่อรู้ว่าภาษาโปรแกรมที่ฉันชอบที่สุดคือ Haskell
- ฉันใช้มันไม่ใช่แค่กับโปรเจ็กต์งานอดิเรก แต่รวมถึงการสร้างเว็บเซิร์ฟเวอร์จริงด้วย
- ฉันกำลังนำทีมต่าง ๆ ที่ทำงานด้วย Haskell ที่ Converge
-
ความเข้าใจผิดเกี่ยวกับ Haskell
- ปัญหาที่แก้ได้ด้วยภาษาสำหรับงานทั่วไปนั้น แน่นอนว่าแก้ได้ด้วยภาษาอื่นเช่นกัน
- ฟีเจอร์จำนวนมากที่ถูกนำเข้าไปใน Python, Rust, Typescript ฯลฯ ได้แรงบันดาลใจจาก Haskell หรือถูกทำไว้ได้แข็งแรงกว่ามาตั้งแต่แรกใน Haskell
- มันดูเหมือนเป็นรูปแบบหนึ่งของแนวคิด "เลือกเทคโนโลยีที่น่าเบื่อ"
- มีความเชื่อที่ผิดว่าการเขียนโปรแกรมไม่ใช่คณิตศาสตร์ และควรตัดองค์ประกอบแบบคณิตศาสตร์ออกไป
-
จุดประสงค์ของบทความนี้
- ต้องการอธิบายอย่างมีเหตุผลว่าทำไม Haskell จึงเป็นตัวเลือกที่ดีที่สุดสำหรับโปรแกรมเมอร์ส่วนใหญ่
- มีประโยชน์เป็นพิเศษสำหรับผู้ที่ต้องการเขียนซอฟต์แวร์ที่แข็งแรงเชื่อถือได้อย่างมีประสิทธิภาพ
- และยังเน้นด้านที่สนุกของการเขียนซอฟต์แวร์ด้วย
-
การเลิกนิสัยเดิมและเรียนรู้ใหม่
- โปรแกรมเมอร์ส่วนใหญ่คุ้นเคยกับพาราไดม์แบบ imperative
- Haskell เป็นภาษา functional แบบบริสุทธิ์ จึงมีเส้นโค้งการเรียนรู้ที่ชัน
- แต่ตัวภาษา Haskell เองนั้น ถ้าจำกัดอยู่ใน subset ที่เรียบง่าย ก็เรียนได้ไม่ยาก
- functional programming ต้องการการเปลี่ยนมุมมองอย่างสิ้นเชิงต่อวิธีประกอบสร้างโปรแกรม
- กระบวนการนี้ช่วยให้เราเติบโตในฐานะโปรแกรมเมอร์
- อ้างคำพูดของ Alan Perlis:
ภาษาใดก็ตามที่ไม่เปลี่ยนวิธีคิดของคุณเกี่ยวกับการเขียนโปรแกรม ก็ไม่คุ้มค่าที่จะเรียนรู้
คำอธิบายไวยากรณ์แบบสั้น ๆ
-
::ใช้ระบุ type signature (เช่นmyThing :: String) -
การเรียกฟังก์ชันไม่ใช้วงเล็บ แต่เขียนอาร์กิวเมนต์ต่อท้ายชื่อฟังก์ชันโดยคั่นด้วยช่องว่าง (เช่น
doSomething withThis withThat) -
ใน type signature ตัวพิมพ์เล็กหมายถึง type variable ซึ่งแทนชนิดข้อมูลใดก็ได้ (เช่น
head :: [a] -> a) -
มีลูกศรอยู่สองแบบคือ
->และ=>:->ใช้อธิบายชนิดของฟังก์ชัน (เช่นadd1 :: Int -> Int)=>ใช้อธิบายข้อจำกัดของ type variable และจะอยู่ก่อนเสมอ (เช่นadd1 :: Num a => a -> a)
-
คอมเมนต์เริ่มต้นด้วย
-- -
returnเป็นฟังก์ชันธรรมดา และไม่ได้มีความหมายแบบที่หลายคนคาดไว้ -
doเป็น syntactic sugar ที่ทำให้โค้ดดูคล้าย imperative -
มีหลายวิธีในการกำหนดค่าให้ตัวแปรภายในขอบเขตย่อย:
let x = <something> in <expression>หรือ
x <- <something> -
ลดความผิดพลาด
- ในหลายภาษา เราต้องเขียน test case จำนวนมากเพื่อทำให้โค้ด "ถูกต้อง"
- Haskell ลดภาระนี้ลงได้มากด้วย type system และ functional programming แบบบริสุทธิ์
- type system ที่ทรงพลังของ Haskell ให้การรับประกันที่เป็นรูปธรรมเกี่ยวกับโปรแกรม และบังคับใช้อย่างเข้มงวด
- คุณลักษณะของ type system:
- ไม่มี nullable type
- สามารถอธิบายการคำนวณที่อาจล้มเหลวได้
- มี pattern matching และการตรวจสอบความครบถ้วน
- หลีกเลี่ยง primitive obsession ได้โดยแทบไม่ต้องออกแรงเพิ่ม
-
ข้อดีของการไม่มีค่า null
- เมื่อไม่มีค่า
nullเราจึงรู้ได้เสมอว่าค่านั้นตรงกับชนิดข้อมูลที่คาดไว้ - ช่วยป้องกัน runtime error และลดพื้นที่ที่อาจเกิดข้อผิดพลาด
- เมื่อไม่มีค่า
-
การแสดงการคำนวณที่อาจล้มเหลว
- ใช้ชนิดข้อมูล
MaybeและEitherเพื่อแสดงอย่างชัดเจนว่าการคำนวณอาจล้มเหลวได้ Maybeแทนการคำนวณที่อาจมีผลลัพธ์หรือไม่มีก็ได้safeHead :: [a] -> Maybe aEitherสามารถมีค่าได้สองแบบ (Left aหรือRight b)validateAddress :: String -> Either AddressParseError ValidAddress
- ใช้ชนิดข้อมูล
-
Pattern matching และการตรวจสอบความครบถ้วน
- ต้องจัดการทุกกรณีของโดเมนอินพุต ไม่เช่นนั้นคอมไพเลอร์จะรายงานข้อผิดพลาด
- สิ่งนี้ช่วยป้องกัน runtime error และเพิ่มความน่าเชื่อถือของโปรแกรม
-
การหลีกเลี่ยง primitive obsession
- ใช้
newtypeเพื่อสร้างชนิดข้อมูลที่มีความหมายเชิงนัยมากขึ้นได้อย่างง่ายดาย
newtype VenueName = VenueName String newtype EventName = EventName String - ใช้
-
ข้อดีของ functional programming แบบบริสุทธิ์
- ข้อมูลไม่เปลี่ยนแปลง จึงไม่ต้องกังวลกับการกลายพันธุ์ของสถานะ
- side effect ถูกจัดการอย่างชัดเจน และฟังก์ชันพึ่งพาเพียงอินพุตโดยไม่มี side effect แฝง
- สิ่งนี้ทำให้โปรแกรมคาดเดาได้มากขึ้นและมีเสถียรภาพมากขึ้น
-
การจัดการ side effect อย่างชัดเจน
- ใช้
IOmonad เพื่อแยกและควบคุม side effect ภายในโค้ด - เราดูจาก type signature ของฟังก์ชันได้เลยว่ามันก่อ side effect หรือไม่
sendGreetings :: User -> IO Response - ใช้
-
Monad และการควบคุม effect
- ใช้ typeclass และ monad เพื่อเข้ารหัสอย่างแม่นยำว่าฟังก์ชันหนึ่งทำ effect อะไรได้บ้าง
- ช่วยป้องกัน side effect ที่ไม่ได้ตั้งใจและเพิ่มความเสถียรของโค้ด
-
ปัจจัยที่ช่วยเพิ่มผลิตภาพ
- ด้วย type system ที่แข็งแรงและคุณสมบัติแบบ pure functional ทำให้ reuse โค้ดและ generalize แนวคิดต่าง ๆ ได้ง่าย
- ผ่านแนวคิดอย่าง
FunctorและMonoidเราสามารถใช้แพตเทิร์นเดียวกันกับโครงสร้างข้อมูลที่หลากหลายได้
fmap (+2) [1, 2, 3] -- [3, 4, 5] fmap (+2) (Just 2) -- Just 4 -
รีแฟกเตอร์ได้อย่างไร้ความกลัว
- ความเข้มงวดของคอมไพเลอร์ช่วยลดความเสี่ยงที่จะสร้างบั๊กใหม่เมื่อแก้โค้ด
- type system ช่วยให้เราแสดงโดเมนของโปรแกรมได้อย่างแม่นยำ จึงแก้ไขโค้ดได้อย่างมั่นใจ
-
เข้าใจโปรแกรมได้ดีขึ้น
- การเขียนแบบ declarative ทำให้แสดงโดเมนของปัญหาได้อย่างตรงไปตรงมา
- ช่วยให้เข้าใจความหมายของโปรแกรมได้ง่ายและเพิ่มความน่าเชื่อถือ
- การตัดความซับซ้อนที่ไม่จำเป็นออกไปทำให้สามารถให้เหตุผลกับโปรแกรมได้อย่างสมเหตุสมผล
-
Algebraic data type และ typeclass
- สามารถสร้าง domain-specific language ภายใน Haskell ได้
- สิ่งนี้ช่วยให้เข้าใจและบำรุงรักษาโปรแกรมได้ง่ายขึ้น
-
ตัวอย่างโปรแกรม
- เขียนเครื่องมือบัญชีอย่างง่ายเพื่อประยุกต์ใช้แนวคิดของ Haskell ในทางปฏิบัติ
บทส่งท้าย
- การใช้ Haskell ทั้งสนุกและมีประสิทธิผล
- การผสานกันของ type system ที่ทรงพลังและสื่อความหมายได้ดี กับ functional programming แบบบริสุทธิ์ คือสิ่งที่ทำให้ Haskell โดดเด่น
- แม้ว่าภาษาอื่น ๆ จะเริ่มนำความสามารถเหล่านี้มาใช้ แต่ใน Haskell คุณลักษณะเหล่านี้เป็นส่วนหนึ่งของแก่นของภาษา
- การเรียนรู้ Haskell จะเปลี่ยนวิธีคิดของคุณต่อการเขียนโปรแกรม
ความเห็นของ GN⁺
-
คุณค่าของการเรียนรู้ Haskell
- ช่วยขยายกรอบความคิดในฐานะโปรแกรมเมอร์
- หากเข้าใจพาราไดม์ functional programming ก็จะเขียนโค้ดในภาษาอื่นได้ดีขึ้นด้วย
-
การเติบโตของ functional programming
- มีจุดแข็งด้าน parallel processing และ concurrency จึงเหมาะกับสภาพแวดล้อมการคำนวณสมัยใหม่
- การควบคุม side effect ช่วยให้เขียนโค้ดที่คาดเดาได้
-
การเปรียบเทียบกับภาษาอื่น
- แม้จะมีภาษาอย่าง Rust หรือ Scala ที่รองรับ functional programming แต่ความบริสุทธิ์และ type system ของ Haskell ยังโดดเด่นเป็นพิเศษ
- แนวคิดของ Haskell ยังมีประโยชน์เมื่อเรียนรู้ภาษาใหม่อื่น ๆ
-
ความเป็นไปได้ในการใช้งานจริง
- แม้ช่วงเริ่มต้นจะมีเส้นโค้งการเรียนรู้ที่ชัน แต่เวลาที่ลงทุนไปจะให้ผลตอบแทนด้านผลิตภาพ
- มีประโยชน์ในระบบที่ซับซ้อนหรือโดเมนที่ไวต่อความผิดพลาด
-
ชุมชนและ ecosystem
- ชุมชน Haskell มีความเคลื่อนไหวสูง และมีการพัฒนาไลบรารีกับเครื่องมือต่าง ๆ อย่างต่อเนื่อง
- สามารถพัฒนาทักษะได้ผ่านการมีส่วนร่วมในโปรเจ็กต์โอเพนซอร์ส
3 ความคิดเห็น
ผมเริ่มต้นกับ F# ซึ่งเพิ่มความใช้งานได้จริงเข้าไป
ADT กับ pattern matching นั้นดีอยู่หรอก แต่ช่วยอย่าพูดถึง monad กับ functor มากนักได้ไหม
ความเห็นจาก Hacker News
Haskell บังคับให้เขียน total function แทน partial function
ใช้ Haskell มา 10 ปีแล้ว และเครื่องมือก็ดีขึ้นมาก
type system ของ Haskell ไม่ได้พิสูจน์ว่าฟังก์ชันเป็น total
ภาษาของ Haskell ดี แต่ ecosystem ยังต้องไปต่ออีกไกล
อยากใช้ Haskell หรือภาษา functional อื่น ๆ ในงานอาชีพ
Haskell มีอิทธิพลอย่างมากต่อวิธีคิดเรื่องการเขียนโปรแกรมและสถาปัตยกรรมของโค้ด
Haskell ทดลองใช้ laziness ในระดับภาษา
ความสุดโต่งของ Haskell ในเรื่องความบริสุทธิ์และ immutability เป็นปัญหา
Haskell เหมาะมากกับซอฟต์แวร์ business logic (BLOBS)