Vì sao lại là Haskell?
(gtf.io)-
"Phi thực tiễn", "hàn lâm", "ngách"
- Đây là phản ứng mọi người thường có khi biết ngôn ngữ lập trình yêu thích nhất của tôi là Haskell
- Tôi dùng nó không chỉ cho các dự án sở thích mà cả để xây dựng web server thực tế
- Tôi đang dẫn dắt các đội ngũ làm việc với Haskell tại Converge
-
Những hiểu lầm về Haskell
- Những vấn đề có thể giải quyết bằng một ngôn ngữ lập trình đa dụng thì ngôn ngữ khác cũng có thể giải quyết được
- Nhiều tính năng được đưa vào Python, Rust, Typescript... đã được truyền cảm hứng từ Haskell hoặc được hiện thực vững chắc hơn trong Haskell
- Điều này có vẻ như một biến thể của tư tưởng "hãy chọn công nghệ nhàm chán"
- Có một quan niệm sai lầm rằng lập trình không phải là toán học, và vì vậy cần loại bỏ các yếu tố mang tính toán học
-
Mục đích của bài viết này
- Nhằm giải thích một cách logic vì sao Haskell là lựa chọn tốt nhất cho phần lớn lập trình viên
- Đặc biệt hữu ích cho những ai muốn viết phần mềm vững chắc một cách hiệu quả
- Đồng thời nhấn mạnh khía cạnh thú vị của việc viết phần mềm
-
Quên đi để học lại
- Phần lớn lập trình viên quen với mô hình mệnh lệnh
- Haskell là một ngôn ngữ thuần hàm nên đường cong học tập khá dốc
- Bản thân ngôn ngữ Haskell khá dễ học nếu giới hạn ở một tập con đơn giản
- Lập trình hàm đòi hỏi một sự chuyển đổi hoàn toàn trong cách cấu thành chương trình
- Quá trình này giúp bạn trưởng thành hơn với tư cách là một lập trình viên
- Trích lời Alan Perlis:
Một ngôn ngữ không làm thay đổi cách bạn nghĩ về lập trình thì không đáng để học.
Giải thích nhanh về cú pháp
-
::biểu thị type signature (ví dụ:myThing :: String) -
Lời gọi hàm không dùng dấu ngoặc mà liệt kê các đối số sau tên hàm, ngăn cách bằng khoảng trắng (ví dụ:
doSomething withThis withThat) -
Trong type signature, chữ thường là biến kiểu, biểu thị một kiểu bất kỳ (ví dụ:
head :: [a] -> a) -
Có hai loại mũi tên là
->và=>:->mô tả kiểu của hàm (ví dụ:add1 :: Int -> Int)=>mô tả ràng buộc lên biến kiểu và luôn đứng trước (ví dụ:add1 :: Num a => a -> a)
-
Comment bắt đầu bằng
-- -
returnlà một hàm thông thường, không mang ý nghĩa như bạn thường nghĩ -
dolà cú pháp đường cho phép code trông giống mệnh lệnh hơn -
Có nhiều cách gán giá trị cho biến cục bộ:
let x = <something> in <expression>hoặc
x <- <something> -
Giảm sai sót
- Trong nhiều ngôn ngữ, người ta viết rất nhiều test case để làm cho code trở nên "đúng"
- Haskell giảm mạnh gánh nặng đó nhờ hệ thống kiểu và lập trình hàm thuần túy
- Hệ thống kiểu mạnh của Haskell cung cấp các đảm bảo cụ thể cho chương trình và thực thi chúng một cách nghiêm ngặt
- Đặc điểm của hệ thống kiểu:
- Không có nullable type
- Có thể biểu diễn các phép tính có thể thất bại
- Pattern matching và kiểm tra tính đầy đủ
- Tránh primitive obsession gần như miễn phí
-
Lợi ích của việc không có giá trị null
- Vì không tồn tại giá trị
null, bạn luôn biết một giá trị có đúng kiểu mong đợi hay không - Điều này giúp ngăn lỗi runtime và giảm bề mặt lỗi
- Vì không tồn tại giá trị
-
Biểu diễn các phép tính có thể thất bại
- Dùng các kiểu
MaybevàEitherđể biểu diễn một cách tường minh những phép tính có thể thất bại Maybebiểu thị một phép tính có thể có hoặc không có kết quảsafeHead :: [a] -> Maybe aEithercó thể mang hai giá trị (Left ahoặcRight b)validateAddress :: String -> Either AddressParseError ValidAddress
- Dùng các kiểu
-
Pattern matching và kiểm tra tính đầy đủ
- Mọi miền đầu vào đều phải được xử lý, nếu không compiler sẽ báo lỗi
- Điều này giúp ngăn lỗi runtime và tăng độ tin cậy của chương trình
-
Tránh primitive obsession
- Có thể dễ dàng tạo ra các kiểu mang nhiều ý nghĩa ngữ nghĩa hơn bằng
newtype
newtype VenueName = VenueName String newtype EventName = EventName String - Có thể dễ dàng tạo ra các kiểu mang nhiều ý nghĩa ngữ nghĩa hơn bằng
-
Ưu điểm của lập trình hàm thuần túy
- Dữ liệu là bất biến, nên không cần lo lắng về đột biến trạng thái
- Side effect được xử lý một cách tường minh, và hàm chỉ phụ thuộc vào đầu vào mà không có side effect
- Điều này làm tăng tính dự đoán và độ ổn định của chương trình
-
Xử lý tường minh side effect
- Dùng
IOmonad để tách biệt và kiểm soát side effect trong code - Có thể biết một hàm gây ra side effect thông qua type signature của nó
sendGreetings :: User -> IO Response - Dùng
-
Monad và kiểm soát effect
- Dùng typeclass và monad để mã hóa chính xác những effect mà một hàm có thể thực hiện
- Điều này giúp ngăn side effect ngoài ý muốn và tăng độ ổn định của code
-
Các yếu tố giúp tăng năng suất
- Hệ thống kiểu mạnh và tính chất thuần hàm giúp việc tái sử dụng code và khái quát hóa khái niệm trở nên dễ dàng
- Thông qua các khái niệm như
FunctorvàMonoid, có thể áp dụng cùng một mẫu cho nhiều cấu trúc dữ liệu khác nhau
fmap (+2) [1, 2, 3] -- [3, 4, 5] fmap (+2) (Just 2) -- Just 4 -
Refactor không sợ hãi
- Nhờ sự nghiêm ngặt của compiler, rủi ro phát sinh bug mới khi thay đổi code sẽ thấp hơn
- Hệ thống kiểu cho phép biểu diễn chính xác domain của chương trình, giúp bạn yên tâm chỉnh sửa code
-
Nâng cao khả năng hiểu chương trình
- Lập trình khai báo cho phép biểu diễn chính xác domain của vấn đề
- Có thể dễ dàng hiểu ý nghĩa của chương trình và tăng độ tin cậy
- Việc loại bỏ độ phức tạp không cần thiết giúp suy luận về chương trình trở nên hợp lý hơn
-
Kiểu dữ liệu đại số và typeclass
- Có thể xây dựng ngôn ngữ đặc thù miền bên trong Haskell
- Điều này giúp ích cho việc hiểu và bảo trì chương trình
-
Chương trình ví dụ
- Viết một công cụ kế toán đơn giản để áp dụng thực tiễn các khái niệm của Haskell
Epilogue
- Dùng Haskell vừa thú vị vừa hiệu quả
- Sự kết hợp giữa hệ thống kiểu mạnh, giàu khả năng biểu đạt và lập trình hàm thuần túy khiến Haskell trở nên đặc biệt
- Các ngôn ngữ khác cũng đang đưa những tính năng này vào, nhưng trong Haskell, chúng nằm ở nền tảng cốt lõi
- Học Haskell sẽ thay đổi cách bạn suy nghĩ về lập trình
Ý kiến của GN⁺
-
Giá trị học tập của Haskell
- Giúp mở rộng phạm vi tư duy với tư cách là một lập trình viên
- Hiểu mô hình lập trình hàm sẽ giúp bạn viết code tốt hơn ngay cả trong các ngôn ngữ khác
-
Sự trỗi dậy của lập trình hàm
- Có thế mạnh về xử lý song song và đồng thời nên phù hợp với môi trường tính toán hiện đại
- Việc kiểm soát side effect giúp có thể viết code có tính dự đoán cao
-
So sánh với các ngôn ngữ khác
- Có những ngôn ngữ hỗ trợ lập trình hàm như Rust hay Scala, nhưng tính thuần khiết và hệ thống kiểu của Haskell vẫn rất khác biệt
- Các khái niệm của Haskell có thể hữu ích khi học những ngôn ngữ mới
-
Khả năng áp dụng trong thực tế
- Đường cong học tập ban đầu khá dốc, nhưng thời gian đầu tư bỏ ra sẽ được đền đáp bằng năng suất cao hơn
- Hữu ích trong các hệ thống phức tạp hoặc các domain nhạy cảm với lỗi
-
Cộng đồng và hệ sinh thái
- Cộng đồng Haskell rất năng động, với nhiều thư viện và công cụ vẫn đang được phát triển liên tục
- Có thể nâng cao kỹ năng bằng cách tham gia các dự án mã nguồn mở
3 bình luận
Tôi đã bắt đầu với F#, ngôn ngữ được bổ sung tính thực dụng.
ADT và pattern matching thì tốt đấy, nhưng xin đừng nhắc đến monad với functor nữa.
Ý kiến Hacker News
Haskell buộc phải viết hàm toàn phần thay vì hàm bộ phận
Đã dùng Haskell 10 năm và công cụ đã cải thiện đáng kể
Hệ thống kiểu của Haskell không chứng minh được rằng hàm là toàn phần
Ngôn ngữ Haskell thì tốt nhưng hệ sinh thái vẫn còn một chặng đường dài
Muốn dùng Haskell hoặc ngôn ngữ hàm khác trong công việc chuyên môn
Haskell đã ảnh hưởng lớn đến tư duy lập trình và kiến trúc mã nguồn
Haskell thử nghiệm tính lười ở cấp độ ngôn ngữ
Tính thuần khiết và bất biến cực đoan của Haskell là một vấn đề
Haskell rất phù hợp với phần mềm logic nghiệp vụ (BLOBS)