19 điểm bởi GN⁺ 2024-09-13 | 3 bình luận | Chia sẻ qua WhatsApp
  • "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à ->=>:

    • -> 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 --

  • return là một hàm thông thường, không mang ý nghĩa như bạn thường nghĩ

  • do là 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
  • Biểu diễn các phép tính có thể thất bại

    • Dùng các kiểu MaybeEither để biểu diễn một cách tường minh những phép tính có thể thất bại
    • Maybe biểu thị một phép tính có thể có hoặc không có kết quả
      safeHead :: [a] -> Maybe a  
      
    • Either có thể mang hai giá trị (Left a hoặc Right b)
      validateAddress :: String -> Either AddressParseError ValidAddress  
      
  • 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  
    
  • Ư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 IO monad để 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  
    
  • 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ư FunctorMonoid, 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

 
cosine20 2024-09-19

Tôi đã bắt đầu với F#, ngôn ngữ được bổ sung tính thực dụng.

 
savvykang 2024-09-13

ADT và pattern matching thì tốt đấy, nhưng xin đừng nhắc đến monad với functor nữa.

 
GN⁺ 2024-09-13
Ý kiến Hacker News
  • Haskell buộc phải viết hàm toàn phần thay vì hàm bộ phận

    • Haskell không ngăn chặn đệ quy vô hạn
    • Trong hệ sinh thái FP đang chuyển sang dependent types, điều quan trọng là bảo đảm trình kiểm tra kiểu không chạy vô hạn
    • Nhiều phần mở rộng tạm thời của Haskell gây ra vấn đề
    • Nếu thích triết lý của Haskell thì không nên chỉ giới hạn ở riêng Haskell
    • Việc chuẩn hóa Haskell đã thất bại
    • Giá trị khác biệt cốt lõi của GHC có thể là hệ thống runtime của GHC
  • Đã dùng Haskell 10 năm và công cụ đã cải thiện đáng kể

    • ghcup, cabal sandboxing, HLS đều ổn định
    • Không thấy nhiều thiếu sót trong hệ sinh thái thư viện
    • Thời gian biên dịch của Haskell vẫn còn bất tiện
    • Thời gian biên dịch dependency khá dài
  • Hệ thống kiểu của Haskell không chứng minh được rằng hàm là toàn phần

    • Trong lập trình thông thường, việc chứng minh tính toàn phần không hữu ích lắm
    • Hầu hết mọi người xác nhận chương trình thực sự hoạt động thông qua kiểm thử
  • Ngôn ngữ Haskell thì tốt nhưng hệ sinh thái vẫn còn một chặng đường dài

    • Trình biên dịch chậm
    • Khả năng báo lỗi còn kém
    • Lỗi đầu tiên làm dừng phần còn lại của quá trình biên dịch
    • Công cụ vẫn còn thiếu so với các ngôn ngữ hàm khác
    • Hệ sinh thái thư viện còn thiếu
    • Các ý tưởng của Haskell đã ảnh hưởng đến nhiều ngôn ngữ khác
  • Muốn dùng Haskell hoặc ngôn ngữ hàm khác trong công việc chuyên môn

    • Các ngôn ngữ như Go khá dễ học
    • Muốn học cách xây dựng codebase bằng ngôn ngữ hàm
  • Haskell đã ảnh hưởng lớn đến tư duy lập trình và kiến trúc mã nguồn

    • Hệ thống kiểu của Haskell rất mạnh và dễ hiểu
    • Việc micro-optimize mã Haskell khá thú vị
    • Công cụ vẫn còn thiếu
  • Haskell thử nghiệm tính lười ở cấp độ ngôn ngữ

    • Tính lười có thể đạt được ở cấp độ thư viện chuẩn
  • Tính thuần khiết và bất biến cực đoan của Haskell là một vấn đề

    • Nhiều lập trình viên thấy vòng lặp dạng thủ tục/có thể thay đổi dễ biểu đạt hơn
    • Rust dùng hệ thống kiểu tốt và nhiều thành ngữ hàm, nhưng vẫn cho phép dùng vòng lặp và tính khả biến
  • Haskell rất phù hợp với phần mềm logic nghiệp vụ (BLOBS)

    • Có thể mô hình hóa phần lớn logic nghiệp vụ bằng kiểu đơn giản và pattern matching
    • Nếu giữ các phần đơn giản, có thể dễ dàng dạy cả cho người đóng góp không chuyên kỹ thuật
    • Haskell rất vui