2 điểm bởi GN⁺ 2023-08-26 | 1 bình luận | Chia sẻ qua WhatsApp
  • Một lập trình viên từng dùng Haskell làm ngôn ngữ chính từ khoảng năm 2009 đến khoảng năm 2019 nay không còn xem Haskell là lựa chọn mặc định cho dự án cá nhân hay khi tìm việc
  • Sức hấp dẫn của Haskell không nằm ở monad hay DSL tự thân, mà ở khả năng suy luận mã theo đại số, tái cấu trúc an toàn nhờ hệ thống kiểu mạnh, và khả năng biểu diễn các bất biến
  • Lý do dần rời xa là do sự tích lũy của văn hóa ưa chuộng các tính năng kiểu nâng cao, công cụ chưa mượt mà, cùng những thay đổi không tương thích liên tục trong GHC và thư viện chuẩn
  • Dù vậy, các hàm bậc cao, cú pháp do, lens, thư viện khai báo, và các trừu tượng kiểu nâng cao vẫn là những ưu điểm mà ngôn ngữ khác khó thay thế dễ dàng
  • Haskell rất đáng học để trở thành lập trình viên giỏi hơn, nhưng nếu chọn làm ngôn ngữ mặc định cho tổ chức thì cần chấp nhận cái giá của chất lượng công cụ và độ ổn định ngôn ngữ

Thời kỳ dùng Haskell làm ngôn ngữ mặc định và vị trí hiện tại

  • Trong khoảng 10 năm, Haskell là ngôn ngữ mặc định cho các dự án mới
    • Sau khi tiếp xúc với Haskell vào khoảng năm 2009, tác giả xem mình là một lập trình viên Haskell cho đến khi chuyển sang nơi làm việc chủ yếu dùng Ruby và C++ vào khoảng năm 2019
  • Haskell được dùng cho nhiều mục đích như công cụ dòng lệnh, dịch vụ web, ứng dụng đồ họa, và các script nhỏ
  • Hiện tại tác giả không hoàn toàn từ chối Haskell, nhưng nó không còn là ngôn ngữ mặc định ngay cả trong dự án cá nhân, và cũng không còn chủ động tìm “công việc Haskell” như trước
  • Nhận định này không phải kết luận rằng Haskell là ngôn ngữ tệ, mà gần hơn với việc giải thích vì sao cá nhân tác giả đã dần rời xa nó

Vì sao từng bị Haskell thu hút

  • Tính chất cho phép xử lý mã theo kiểu đại số

    • Điểm hấp dẫn lớn nhất của Haskell là có thể suy luận mã một cách ký hiệu và đại số
    • Ví dụ, nếu có hàm double n = n + n thì phép biến đổi double x thành x + x trong Haskell luôn là đúng
    • Trong nhiều ngôn ngữ mệnh lệnh, double(i++)(i++) + (i++) có thể cho kết quả khác nhau
      • Trường hợp đầu tăng i một lần
      • Trường hợp sau tăng i hai lần
    • Nhờ tính chất này, có thể tích lũy các phép biến đổi cơ học nhỏ để thực hiện những lần tái cấu trúc lớn an toàn hơn
    • Việc tái cấu trúc lớn trong codebase Ruby có thể rất bất an, nhưng trong codebase Haskell thì dễ chịu hơn nhiều
  • Hệ thống kiểu nghiêm ngặt nhưng không quá ồn ào

    • Hệ thống kiểu của Haskell rất nghiêm ngặt nhưng nhờ suy luận kiểu, không cần viết quá nhiều chú thích kiểu như Java
    • Typeclass cũng giúp giảm bớt phiền toái trong các tình huống phải quan tâm tới kiểu
    • Mã Haskell tốt có tính chất “dễ vỡ đúng cách” (brittle)
      • Một trừu tượng sai không âm thầm bị bẻ cong để cho phép chương trình sai chạy qua
      • Nếu có vấn đề, quá trình biên dịch sẽ thất bại
    • Kiểu NonEmpty biểu diễn danh sách có ít nhất một phần tử
      • Các phép toán giữ nguyên độ dài như map sẽ trả về NonEmpty
      • Các phép toán làm tăng độ dài như append cũng trả về NonEmpty
      • Các phép toán mà kết quả có thể rỗng như filter sẽ trả về danh sách thường
    • Nếu một hàm yêu cầu danh sách đường dẫn tệp phải là NonEmpty, thì không thể truyền trực tiếp đối số dòng lệnh vào đó mà phải kiểm tra trước là không rỗng
    • Nếu sau này thêm bước lọc theo phần mở rộng, kết quả có thể rỗng nên phải kiểm tra lại, và trình biên dịch sẽ chặn các đường đi vô tình truyền danh sách rỗng
  • Duy trì bất biến bằng kiểu

    • Dùng Haskell lâu dần sẽ quen với cách biểu diễn bất biến của chương trình bằng kiểu và để trình biên dịch xác minh
    • Điều này không chỉ có nghĩa là các phép tính phức tạp ở mức kiểu
    • Có thể phân biệt trạng thái đã escape SQL hay chưa bằng các kiểu bọc như RawStringSanitizedString
      • Dù web API trả về RawString, tầng cơ sở dữ liệu có thể được thiết kế chỉ nhận SanitizedString
    • Cũng có thể chuyển từ biểu diễn bề mặt với nhiều trường Maybe sang biểu diễn nội bộ bảo đảm rằng thông tin cần thiết luôn tồn tại
    • Ngoài ra còn có thể chỉ làm cho mã generic ở mức đủ để kiểm thử độc lập
  • Một ngôn ngữ tự nhiên cho DSL và hàm bậc cao

    • Haskell giúp tạo ra ngôn ngữ chuyên biệt theo miền ngắn gọn ngay cả khi không có macro
    • config-ini là một ví dụ về thư viện xử lý tệp cấu hình INI
      • Thay vì giao diện phân tích kiểu mệnh lệnh, nó dùng giao diện khai báo
      • Ánh xạ một phần cụ thể của kiểu cấu hình vào một phần cụ thể của cấu trúc tệp INI
      • Cùng một giao diện được dùng cho đọc, ghi, và cập nhật tối thiểu theo diff
    • DSL trong Ruby có thể dựa vào metaprogramming động và monkeypatching toàn cục, còn DSL trong Haskell dễ giới hạn vào một tệp hay vùng mã cụ thể hơn nhờ cú pháp linh hoạt và các tính năng như overload monad
    • Haskell cũng khiến việc dùng hàm bậc cao trở nên tự nhiên
      • (+) có thể được dùng ngay như một hàm nhận hai đối số số và cộng chúng
      • Có thể biểu diễn phép nhân từng cặp phần tử của hai danh sách rất ngắn gọn như pairwiseProduct = zipWith (*)
    • Có thể ví Haskell với Perl theo nghĩa: Perl khiến việc thao tác chuỗi trở nên thuận tay, còn Haskell khiến việc thao tác hàm trở nên thuận tay

Ba yếu tố khiến dần rời xa Haskell

  • Bầu không khí ưa chuộng phong cách khó nhằn

    • Yếu tố đầu tiên là sự ưa chuộng cái mới trong phong cách
    • Cộng đồng Haskell liên tục thử nghiệm và xây dựng các trừu tượng mới
      • Những cách tiếp cận lấy cảm hứng từ đại số trừu tượng hay lý thuyết phạm trù
      • Những cách tiếp cận đẩy nhiều tính toán hơn lên mức kiểu để giới hạn trạng thái sai
    • Type-level web API của Servant hay trường hợp biểu diễn hệ thống menu bằng comonad là các thử nghiệm nhằm biểu diễn miền bài toán theo cách mới
    • Nhưng trong mã công việc hằng ngày, những cách tiếp cận mang tính thử nghiệm như vậy thường tạo ra nhiều vấn đề hơn lợi ích ban đầu mà chúng thể hiện
    • Trong một ví dụ ở dự án trình biên dịch, các nút AST mang kiểu biểu thức như tham số kiểu
      • Nếu tạo ra cây cú pháp có lỗi kiểu, bản thân trình biên dịch sẽ không biên dịch được
      • Ban đầu điều này có bắt được một số lỗi
      • Nhưng khi nhiều pass tối ưu hóa không khớp với kỷ luật kiểu, mã trở nên phức tạp vì phải ép kiểu và xoa dịu GHC
      • Cuối cùng tham số kiểu đã bị loại khỏi AST
    • Trong khoảng ba phần tư các dự án từng thử hướng “fancy types”, tác giả đánh giá rằng giá trị nhận lại không đủ đáng
    • Trào lưu Simple Haskell dựa trên ý tưởng rằng có thể nhận được nhiều lợi ích nhất của ngôn ngữ khi tránh các tính năng thử nghiệm và chỉ dùng số lượng extension tối thiểu
    • Các tính năng kiểu phức tạp hơn đã lan rộng trong cộng đồng và thư viện
    • Các trừu tượng kiểu phức tạp có thể khiến việc thay đổi và bảo trì khó hơn, và tác giả từng trải nghiệm việc cố thay một trừu tượng đã đơn giản hóa bằng trừu tượng kiểu cấp cao mới hơn dẫn đến mã lớn hơn, hiệu năng kém hơn, và khó đọc hơn
    • Tuy vậy, những tính năng kiểu mức cao “cầu kỳ” như Trees that Grow, nơi cấu trúc dữ liệu được đánh chỉ số theo từng giai đoạn của trình biên dịch, vẫn là ví dụ hữu ích
  • Công cụ không thật sự tỏa sáng trong công việc hằng ngày

    • Yếu tố thứ hai là hệ công cụ gượng gạo
    • Haskell có nhiều hệ thống build và công cụ phát triển gần như đóng vai trò tiêu chuẩn
    • Nhìn chung các công cụ này làm được việc cần làm, nhưng chưa đạt tới mức “đáng để yêu thích”
    • Có những công cụ như ormolu hoàn thành nhiệm vụ với mức phiền toái tối thiểu, nhưng một số công cụ khác đòi hỏi phải liên tục đối phó với bug và tìm đường vòng
    • Công cụ Haskell đã cải thiện trong 10 năm, nhưng tính đến năm 2019 vẫn chưa có công cụ nào đạt mức dễ dùng và ổn định như Rust Cargo
    • Xu hướng Not-Invented-Here, luôn muốn tạo ra thứ mới và tốt hơn, cũng là một vấn đề
      • Thậm chí còn có xu hướng xử lý lại cả định dạng văn bản gói bằng hpack
      • Thật khó chịu khi những nỗ lực như vậy cứ ở trạng thái hoàn thành 95% trong nhiều năm
    • Đây không phải nhận xét rằng công cụ Haskell là tệ hại, nhưng mức “cũng tạm ổn” không còn đáp ứng tiêu chuẩn đã tăng lên
  • Những thay đổi không tương thích kéo dài

    • Yếu tố thứ ba là sự thay đổi liên tục của ngôn ngữ Haskell và GHC
    • Trên thực tế, GHC được xem như triển khai Haskell công khai duy nhất đủ hợp lý
    • Haskell cố gắng dần sửa các sai lầm và điểm thiếu nhất quán trong quá khứ, kể cả ở thư viện chuẩn và những phần nền tảng của ngôn ngữ
    • Những thay đổi này có thể đưa ngôn ngữ tới một nền tảng sạch sẽ và nhất quán hơn, nhưng với người dùng thì lại tạo ra ma sát nhỏ liên tục
    • Foldable-Traversable Prelude changes năm 2015 là một ví dụ cho kiểu thay đổi này
    • Các dự án Haskell cũ có thể không build được trên GHC mới ngay cả khi không có phụ thuộc bên ngoài
      • thêm superclass
      • xóa import
      • thêm type signature vì polymorphism trở nên mơ hồ
    • Các chỉnh sửa thường nhỏ, nhưng cứ tích tụ dần và những chỗ hơi khác so với trước liên tục đòi hỏi người dùng phải để ý
    • Điều này tương phản với cách cập nhật theo epoch của Rust, nơi có cam kết tương thích ngược cho các tính năng ổn định và các breaking change có chủ đích được giấu sau từng epoch

Những ưu điểm của Haskell vẫn còn được nhớ tới

  • Cách viết mã và tái cấu trúc theo kiểu đại số của Haskell vẫn là một thế mạnh
    • Rust cũng chia sẻ nhiều ưu điểm với Haskell, nhưng để chắc chắn rằng một lần tái cấu trúc cơ học nhỏ vẫn giữ nguyên ý nghĩa chương trình thì không dễ như trong Haskell
  • Hệ thống kiểu của Haskell cũng vẫn cung cấp những khả năng mà các ngôn ngữ khác thường không có
    • Các ngôn ngữ chủ đạo năm 2023 đã tiến bộ rất nhiều về kiểu tĩnh so với năm 2009
    • Dù vậy, các tính năng như những trừu tượng xây trên higher-kinded types vẫn là điểm khác biệt của Haskell
  • Hệ sinh thái thư viện Haskell khá lẫn lộn
    • các thư viện mã nguồn mở được duy trì nửa vời
    • những vương quốc nhỏ nơi từng tác giả riêng tái kiến tạo thế giới theo cách của mình
    • nhiều kiểu gần giống chuỗi khác nhau, đều cần thiết nhưng không tương thích nhau
  • Dù vậy, Haskell vẫn có các thư viện và trừu tượng tuyệt vời mà ở ngôn ngữ khác chỉ làm được một nửa
    • lens là một trừu tượng mạnh, khó thấy hết lợi ích nếu chưa dùng đủ lâu
    • Brick cung cấp một thư viện UI terminal rất tốt bằng các khối khai báo
    • diagramsmusic-suite cũng là những ví dụ hay về API khai báo
  • Cú pháp do cũng là một tính năng rất đáng nhớ
    • Điều cốt lõi không hẳn là bản thân do, mà là một kiểu ký pháp cho phép thêm binding mà không khiến mức thụt lề cứ tăng mãi
    • flat_map lồng nhau trong Ruby sẽ khiến thụt lề ngày càng sâu khi số chiều tăng lên
    • Cú pháp do trong Haskell biểu diễn cùng một cách kết hợp đó theo dạng phẳng
  • Cú pháp do còn cho phép nhúng những thứ như đồng thời có cấu trúc kiểu async/await, giao dịch đồng thời, hay chương trình xác suất dưới cùng một lớp cú pháp nhẹ nhàng

Có nên học hay dùng Haskell không

  • Câu trả lời cho “có nên học hay dùng ngôn ngữ này không” phụ thuộc vào mục tiêu
  • Nếu mục tiêu là trở thành lập trình viên giỏi hơn, Haskell rất đáng học
    • Haskell là một ngôn ngữ thú vị và mạnh mẽ
    • Tác giả đánh giá rằng việc học Haskell đã nâng cao năng lực lập trình của mình
  • Nếu mục tiêu là sử dụng thực tế, thì nên cân nhắc kỹ hơn
    • Các ưu điểm của Haskell là có thật
    • Cần so sánh chúng với mức ma sát mà cá nhân hay tổ chức sẵn sàng chấp nhận như đã nói ở trên
    • Vẫn có những cá nhân và tổ chức nhận được đủ lợi ích từ Haskell để giữ các bất tiện nhỏ chỉ ở mức vết xước nhỏ
  • Nếu buộc phải trả lời dứt khoát có hay không, thì câu trả lời nghiêng về “đừng dùng Haskell”
    • Nếu phải xây dựng một tổ chức kỹ thuật, tác giả sẽ không chọn Haskell làm ngôn ngữ mặc định
    • Vì tác giả hiểu cả giá trị của công cụ tốt lẫn chi phí của công cụ tệ
    • Tác giả ưu tiên các ngôn ngữ có thể cung cấp ngay một môi trường ngôn ngữ ổn định, hệ sinh thái mã rõ ràng, và công cụ xuất sắc
  • Dù vậy, Haskell vẫn cung cấp những công cụ tuyệt vời cho độ an toàn và tính đúng đắn của chương trình
    • Trong một số bối cảnh nhất định, có thể vẫn đáng chấp nhận công cụ chưa tốt và linting để ngăn những type family không cần thiết
  • Haskell không còn là ngôn ngữ phù hợp với cá nhân tác giả nữa, nhưng tác giả kết luận rằng nếu Haskell không tồn tại trên đời thì thế giới sẽ tệ hơn

1 bình luận

 
GN⁺ 2023-08-26
Ý kiến trên Hacker News
  • Tôi đã dùng Haskell khoảng 10 năm rồi gần đây rời khỏi nó như một phương tiện kiếm sống, và chắc chắn đồng ý với điểm đầu tiên của tác giả: cộng đồng Haskell rất coi trọng việc học hỏi
    Làm việc với những người tò mò, nơi luôn có điều để dạy và học, là điều tốt, nhưng cộng đồng lại khá yếu trong việc bỏ đi các ý tưởng đã thử; vì vậy các codebase Haskell chuyên nghiệp, nếu không được quản lý nghiêm ngặt, rất dễ tích tụ nhiều thứ kiểu “làm được, nhưng tốt nhất là không nên dùng”
    Tuy nhiên, về toolchain thì tôi có ý kiến khác. Đúng là công cụ của Haskell không hay lắm, nhưng sau khi dùng Python, JS, Java, Rust, Elm, tôi thấy phần lớn công cụ cũng chẳng hay, và ngược lại còn nhớ toolchain của Haskell. cargo rất tuyệt nhưng gần như là ngoại lệ, và tôi tò mò liệu Rust có tránh được tình trạng này khi nó già đi hay không

    • Tôi đã dùng nhiều ngôn ngữ, nhưng công cụ của Haskell gần như thuộc loại tệ nhất. language server thường xuyên hỏng vì lỗi ngẫu nhiên, và lần nào cũng bối rối không biết nên giao việc quản lý phiên bản Haskell cho ghcup hay stack
      Có một dự án tôi thỉnh thoảng làm, và tôi chắc 100% rằng lần sau mở ra nó sẽ lại không chạy. Quản lý phụ thuộc và môi trường ảo của Python cũng tệ tương tự, nhưng Poetry khá ổn; Ruby thì dù dùng chruby/rbenv/rvm cái nào cũng hoạt động tốt tương tự, Bundler cũng chạy ngon. Java có khuyết điểm, nhưng trải nghiệm IDE thuộc hàng tốt nhất, và dù refactoring không an toàn bằng Haskell, nó vẫn rất dễ thực hiện
    • Cargo là sự tiến hóa từ các ý tưởng cũ như Bundler của Ruby, nên trong loại công cụ này, nó có phả hệ khá dài
      Bundler nhìn chung không tệ; dù có những lựa chọn thiết kế tôi không đồng ý, nếu đi theo đúng đường thì rất nhiều thứ khớp với nhau tốt. Công cụ ngôn ngữ cũng có thể được làm theo cách đủ ít hỏng hóc; tôi chưa dùng cargo nhiều trực tiếp, nhưng nếu nó thật sự là tốt nhất trong lớp thì việc nó duy trì trạng thái đó cũng không đáng ngạc nhiên
    • Hơi khó tin khi công cụ Java bị xếp vào nhóm “không hay”. Có thể nói nhiều điều tiêu cực về bản thân Java, nhưng công cụ miễn phí lẫn thương mại của nó thì ở đẳng cấp vượt trội
    • Công cụ của Haskell đã làm tôi mất hứng đào sâu lại một cách nghiêm túc. Ngược lại, Purescript mỗi lần tôi thử vài lần đều cho trải nghiệm khá mượt, nhất là nếu đã quen với công cụ JS
    • Tôi thắc mắc vì sao các cuộc bàn luận về công cụ lại thiếu ngôn ngữ .NET hay Swift. Có thể vì các IDE như Visual Studio hay Xcode là công cụ hạng nhất, hoặc cũng có thể vì thế giới nguồn mở có xu hướng tránh phụ thuộc vào Big Tech
  • Về mặt công cụ, Haskell có một thứ mà theo tôi biết thì các ngôn ngữ khác khó cạnh tranh: Hoogle
    Hoogle tốt đến mức đáng kinh ngạc. Bạn nói bằng Haskell điều mình muốn, nó cho bạn biết những gì có thể làm bằng Haskell. Rust cũng từng có những nỗ lực tương tự, và tôi cũng từng thử làm Noogle cho Nim, nhưng ở các ngôn ngữ mà “truyền đối số cho hàm” và “gọi hàm” được phân tách rõ ràng, nó không hoạt động theo cùng cách
    Mỗi khi nhìn code Rust có Result, Box đan xen, tôi lại ước có một Roogle hữu ích như Hoogle. Ngoài ra, công cụ Haskell không có ưu điểm gì. Phần lớn có vẻ là do bản năng của cộng đồng muốn tạo ra thứ gì đó “đột phá” thay vì cải thiện cái đã có

    • Tôi rất đồng ý rằng Hoogle cực kỳ hữu ích. Tuy vậy, về các công cụ khác, tôi dùng haskell-language-server hằng ngày và năng suất đã tốt hơn rất nhiều
      Nó chưa hoàn hảo, nhưng so với 5 năm trước thì tốt hơn hẳn
    • Hoogle thật sự tuyệt vời. Lấy cảm hứng từ đó, tôi cũng đã triển khai một thứ tương tự cho thư viện lập trình hàm FunctionalPlus dành cho C++: https://www.editgym.com/fplus-api-search/
      Tôi mong nhiều dự án hơn đi theo hướng này
  • Tôi nghĩ vấn đề lớn nhất là GHC bị ràng buộc với một phiên bản base cụ thể, vốn là thư viện chuẩn
    Khi base thay đổi và phiên bản GHC mới chỉ hỗ trợ phiên bản đó, về cơ bản bạn phải thay đổi mọi phụ thuộc để dùng GHC mới hơn. Tôi vẫn chưa hiểu vì sao code biên dịch bằng GHC 9.6 lại phải dùng base 4.18.0.0. Tại sao binary GHC phải quan tâm đến phiên bản module Data.List của code được biên dịch
    Tôi hiểu rằng các thành phần dành riêng cho GHC mà base phơi bày thì bị ràng buộc với một phiên bản GHC cụ thể, nhưng không rõ vì sao phần còn lại cũng phải như vậy. Tuy nhiên, tôi hiểu là đang có công việc chia base thành nhiều package để sửa điều này: https://gitlab.haskell.org/ghc/ghc-wiki-mirror/-/blob/master...

    • Phần được trích dẫn có gợi ý. GHC mới hơn có thể từ chối code base cũ vì coi nó không hợp lệ
    • Vì các kiểu dữ liệu nội bộ có thể khác nhau. Nếu các thư viện được liên kết với các triển khai base khác nhau trao đổi các instance Data.List cho nhau thì sẽ phát sinh vấn đề
      Ví dụ, khi có một danh sách của Data.List, liệu bạn có thể nối kết quả của hai hàm thư viện khác nhau vào danh sách đó không
    • Như đã chỉ ra, base có thể cài đặt lại là mục tiêu mà mọi người đều mong muốn. Tình trạng hiện tại chủ yếu do lý do lịch sử và do các phần tử cơ bản gắn với nội bộ compiler bị kết hợp với các phần tử không cơ bản; nếu bỏ đủ công sức thì có thể cải thiện được
  • Nếu có ba lý do lớn khiến sự quan tâm đến Haskell dần nguội đi, thì đó là khuynh hướng chuộng cái mới về phong cách, tôn vinh những đoạn mã khó hiểu khiến việc bảo trì trở nên khó khăn; công cụ gượng gạo làm các công việc hằng ngày chậm chạp hơn; và những thay đổi liên tục, dù chỉ thỉnh thoảng nhưng vẫn đều đặn phải để tâm và định kỳ phá vỡ mọi thứ
    Đây là nhận xét xác đáng. Vào giai đoạn 2010–2012 tôi học Haskell khá nhiều; bản thân ngôn ngữ thì tuyệt vời, nhưng tài liệu và công cụ rất khó xử lý. Cộng đồng đi từ Cabal và “Cabal hell” khét tiếng sang Stack, rồi lại quay về Cabal, và nhìn chung mọi thứ đã tốt hơn
    Trong khi đó, các ngôn ngữ lập trình khác cũng đã hấp thụ các yếu tố lập trình hàm. Ví dụ Java đã thêm Streams, hàm, lambda, kiểu dữ liệu đại số, records, pattern matching; cú pháp có thể không thanh nhã như Haskell, nhưng các khái niệm cơ bản của lập trình hàm đều đã có mặt

    • Trong nỗ lực tuyệt vọng nhằm thoát khỏi nhà tù, các tù nhân đã tiếp nhận những nghi lễ kỳ quái lấy cảm hứng từ ánh sáng nhìn thấy qua các song sắt hẹp, qua đó làm vấy bẩn ngôn ngữ của mình
      Như thế họ tự tạo ra một hố đau khổ vô tận cho chính mình, và nó chỉ vừa đủ chịu đựng được vì chùm sáng không xuống đủ thấp để thấy được màu sắc mà ánh sáng tạo ra trên cái cây ngoài cửa sổ. Những người đến từ bên ngoài nhanh chóng mất trí, bị xé giữa phương ngữ đã thích nghi với bóng tối do họ tự tạo ra và phương ngữ có thể phát triển mạnh trong ánh sáng nhưng lại ngạt thở ở nơi đó
    • Cấu trúc dữ liệu bất biến hiệu quả quan trọng với lập trình hàm hơn hầu hết các tính năng được liệt kê. Java cung cấp gì ở điểm đó?
    • Tôi không biết chuyện từ Cabal sang Stack rồi lại quay về Cabal. Tôi đã rời Haskell vài năm, và lần cuối dùng thì Stack có vẻ là thứ trơn tru nhất, giải quyết được nhiều nỗi đau của Cabal
      Nếu cộng đồng đã quay lại Cabal, tôi tò mò không biết mình đã bỏ lỡ điều gì
  • Vì những lý do này mà tôi chuyển sang F# và hầu như không ngoái lại. Thỉnh thoảng tôi cũng nhớ higher-kinded types, nhưng F# cũng có generics, và nói thật thì nó có phần buộc tôi viết mã đơn giản hơn so với khi dùng Haskell, nên nhìn chung kết quả tốt hơn
    Tuy vậy F# không đem lại cảm giác “cao thủ” như Haskell. Nó giống Haskell nhưng hoàn toàn thực dụng. Tôi làm được rất nhiều việc bằng F# và dùng nó rất vui, nhưng đôi khi vẫn bâng khuâng nhìn lại và tự hỏi vì sao mình với Haskell lại không thành

    • Thật vui khi thấy câu chuyện như thế này. Tôi mới bước vào lập trình hàm và đang thích F#, vì nó súc tích và tập trung vào thực tế
      Tôi thường tự hỏi liệu không dùng Haskell có phải là đang bỏ lỡ điều gì không, nhưng với mục đích của tôi thì có lẽ không
  • Học Haskell giống như cố giải mã một ngôn ngữ ngoài hành tinh. Nếu bạn đã quen với vòng lặp if-else bình thường và phép gán biến đơn giản, hãy chuẩn bị cho đầu óc bị xoắn lại
    monad không phải quái vật vũ trụ mà là một khái niệm trừu tượng kỳ lạ, có thể khiến bạn gãi đầu và nghi ngờ các lựa chọn đời mình. Thư viện thì giống như lục lọi một cửa hàng đồ cũ vơi một nửa để tìm thứ mình cần, còn tài liệu thì một nửa đọc như chữ tượng hình
    Hiệu năng trong thực tế cũng có thể khiến bạn phải suy nghĩ rất lâu vì lazy evaluation làm chương trình ăn bộ nhớ nhiều hơn dự kiến và chậm đi. Việc làm cũng khó tìm, trừ khi là một công ty cực kỳ ngách dồn toàn lực vào Haskell, còn debug thì giống như mò kim đáy bể trong một đống chữ tượng hình kiểu hàm

  • Tôi đã dùng Scala, và thấy nó có thể đem lại điểm hay của cả hai thế giới. Có thể suy luận theo kiểu đại số, và nếu sau khi refactor mà biên dịch được thì thường nó sẽ chạy
    Type inference và monad cũng hoạt động tốt, đồng thời có lợi thế từ hệ sinh thái Java. Khi mọi thứ trở nên quá khó hiểu, bạn cũng có thể phá luật và viết code gần với Java hơn. Tôi tò mò người khác nghĩ sao

    • Tôi cũng rất thích dùng Scala vì cùng lý do. Có lối thoát khi cần và hệ sinh thái cũng lớn
      Tuy vậy sbt khiến tôi phát điên, và có lẽ là phần tôi không thích nhất trong trải nghiệm phát triển
    • Đồng ý. Tôi cũng thích việc nó cho phép chuyển dần một cách mượt mà từ code mệnh lệnh/OOP sang phong cách hàm thuần túy
  • Những người quản lý và bảo trì Haskell thực sự có vẻ muốn ngôn ngữ này thân thiện với nghiên cứu, thử nghiệm và khám phá học thuật
    Bản thân điều đó không sao, nhưng đến một lúc nào đó chắc chắn sẽ xung đột với lợi ích của các lập trình viên muốn dùng Haskell như một công cụ thực dụng và ổn định. Điều cần thiết có lẽ là khả năng đánh dấu các thứ mang tính thử nghiệm và tiên phong vào một track riêng. Dù là pragma hay khai báo ở cấp package, chỉ nhìn package cũng phải biết có nên kéo nó vào hay không
    Như vậy các lập trình viên thiên về thực tiễn có thể áp dụng chính sách kiểu “chúng tôi chỉ dùng stable Haskell”. Cần thừa nhận động lực “Avoid success at all costs”, rằng nếu Haskell được áp dụng quá rộng thì ngôn ngữ sẽ bị quán tính kìm lại và không thể thúc đẩy các khái niệm tiên phong; nhưng vẫn cần một track chính thức để lập trình viên hằng ngày có thể ở trong một tập con Haskell hạn chế hơn và ổn định hơn

    • Đúng vậy. Nguyên tắc này được nêu là nguyên tắc cốt lõi trong A History of Haskell: Being Lazy with Class (2007). Liên kết PDF trực tiếp: https://www.microsoft.com/en-us/research/wp-content/uploads/...
    • Vai trò đó chính là Stackage. Stackage cung cấp một tập hợp nhất quán các package Haskell được biết là build cùng nhau và vượt qua kiểm thử, trở thành các snapshot Stackage Nightly và các bản phát hành LTS (Long Term Support)
      Java sẽ không bao giờ có được thứ như vậy. https://www.stackage.org/
    • Có sáng kiến Simple Haskell khuyến khích hướng đi đó. Tuy nhiên không có flag hay pragma nào để nói “dự án này dùng simple Haskell”
      Rốt cuộc, sự đơn giản phụ thuộc vào người nhìn. Ngay cả các tính năng kiểu hào nhoáng cũng có cách dùng khiến kiểu biểu đạt hơn, code an toàn hơn và thậm chí đơn giản hơn, nếu bạn đã nội hóa được cách chúng hoạt động
  • Bài viết khá hay. Nhưng điểm yếu là tôi chưa từng cảm thấy bị thúc ép phải dùng một tính năng kiểu mới mẻ, hào nhoáng nào đó nếu mình không muốn
    Nếu không thích API HTTP ở cấp kiểu của Servant, có thể hạ xuống một tầng và dùng warp. Các thư viện hiểu rằng các kiểu quá phức tạp có thể trở thành cái bẫy, nên chúng thường được phân tầng theo kiểu này. Vẫn cần trực giác để biết nên đi xa đến đâu, và quá trình đó cũng bao gồm cả sai lầm

    • Tôi không rành Haskell, nhưng ý tưởng của Servant trông cực kỳ hay; không biết có bài nào kiểu “làm ơn dạy tôi với” dành cho người như vậy không
      Tôi đã đọc tài liệu Servant, nhưng hiển nhiên nó giả định mức hiểu biết về Haskell nhiều hơn những gì tôi có
  • Bài này thật sự chạm đúng cảm giác của tôi. Tôi đã dùng Haskell khoảng 1,5 năm trong một ứng dụng khá mang tính công nghiệp, và trước đó cũng có kha khá trải nghiệm như https://github.com/mattgreen/hython
    Trong 20 năm sự nghiệp, đã lâu rồi tôi mới phải học nhiều đến vậy; đó là kết quả của việc miền bài toán và ngôn ngữ cộng lại. Vừa phấn khích vừa mệt mỏi, và cũng có thể là một khối lượng khá lớn để gánh cùng với đời sống gia đình bận rộn
    Dù vậy ngôn ngữ này rất xuất sắc. Quản lý của tôi thường khuyên dùng nó theo kiểu top-down, nghĩa là vì sự linh hoạt của Haskell truyền tải ý định phát triển rất tốt, hãy suy nghĩ kỹ xem mã nên được đọc như thế nào rồi bắt đầu từ đó. Đây là một thay đổi tư duy lớn so với việc gõ đi gõ lại function trong hầu hết ngôn ngữ khiến não chuyển sang chế độ tiết kiệm năng lượng, và nó thật sự có cảm giác được thiết kế để thân thiện với người viết
    Nhưng ngay cả khi xử lý các effect được kiểm soát bằng monad, cú pháp vẫn khó đọc lướt. Có lẽ là vì một dòng có thể trở nên quá đặc. Tôi rất nhớ cơ chế trả về sớm, và cũng nhớ LSP tuyệt vời của Rust. Haskell gần đây còn mất cả tính năng điền pattern match do các thay đổi nội bộ ở bản 9.x; việc chồng monad vẫn khiến tôi ghét và tôi thường làm sai. Thời gian biên dịch có thể rất khắc nghiệt, đặc biệt khi gặp thư viện lens
    Tôi cũng không thích mặc định đánh giá lười. Cộng đồng ở mức nào đó cũng đã thừa nhận rằng vì mặc định này, chương trình Haskell dễ bị rò rỉ bộ nhớ dạng space leak hơn nhiều, và trong nhiều chương trình điều đó có thể không bị phát hiện trong khá lâu. Với tư cách một lập trình viên hệ thống, điều này khiến tôi muốn hét lên
    Dù vậy cộng đồng này là một trong những nhóm lập trình viên mạnh nhất mà tôi từng thấy. Tôi không muốn cứ nói theo meme rằng họ thông minh, mà điểm lớn là họ thật sự suy nghĩ rất sâu và đẩy mọi thứ tiến lên. Trong miền của chúng tôi là phân tích chương trình, Haskell hợp như găng tay, nhưng ở các lĩnh vực khác tôi vẫn hơi thận trọng. Về cơ bản tôi vẫn khát một ngôn ngữ lập trình kiểu OCaml nhưng có cú pháp tốt hơn

    • Có những lúc trả về sớm thật sự cần thiết cho khả năng đọc. Khi đó tôi khuyên dùng ExceptT: https://www.stackage.org/haddock/lts-21.8/mtl-2.2.2/Control-...
      Cũng không cần để lộ ExceptT trong interface; chỉ dùng nó nội bộ cho trả về sớm rồi để hàm trả về Either là được
    • Bạn không phải người duy nhất muốn “một ngôn ngữ lập trình như OCaml nhưng có cú pháp tốt hơn”. Tôi cũng vậy, nên khi Reason mới xuất hiện tôi đã đào sâu vào nó
      Tiếc là cuối cùng nó không đạt được nhiều traction
    • Đó chính là Scala 3. Nó có cú pháp giống Python, và nhờ JVM nên có hệ sinh thái thư viện lớn hơn Haskell hay OCaml rất nhiều
      Việc tích hợp lập trình hướng đối tượng và lập trình hàm tốt hơn OCaml, và mã OCaml theo idiom cũng đủ tương tự để dễ chuyển sang bằng một phép biến đổi đơn giản