8 điểm bởi GN⁺ 2025-08-15 | 2 bình luận | Chia sẻ qua WhatsApp
  • Đặc tính ngôn ngữ và hệ sinh thái của OCaml rất xuất sắc, phù hợp cho cả dự án cá nhân lẫn chuyên nghiệp
  • Các tính năng đa mô hình và nâng cao như hệ thống kiểu tĩnh, kiểu đại số, hệ thống mô-đun, mô hình đối tượng, hiệu ứng do người dùng định nghĩa được tích hợp ổn định
  • Có bộ công cụ trưởng thành gồm trình quản lý gói OPAM, hệ thống build Dune, hỗ trợ biên tập LSP/Merlin, công cụ tài liệu hóa Odoc, cùng hệ sinh thái thư viện đa dạng cho web, blockchain, tooling và hơn thế nữa
  • Cộng đồng có tính tiếp cận, sự thân thiện và tính chuyên môn, giúp việc học và cộng tác trở nên dễ dàng, đồng thời triển vọng tương lai cũng sáng sủa nhờ quá trình tiến hóa bền bỉ

Vì sao tôi chọn OCaml làm ngôn ngữ chính

  • Tác giả đã sử dụng nhiều ngôn ngữ lập trình trong thời gian dài, và trong số đó đã chọn OCaml làm ngôn ngữ chính
  • Ưu điểm lớn nhất của OCaml được xem là hệ thống kiểu tĩnh mạnh mẽ cùng khả năng hỗ trợ lập trình hàm vượt trội so với C hoặc nhiều ngôn ngữ hàm khác
  • Nhờ hệ thống kiểu nói trên, tác giả đã nhiều lần trải nghiệm việc ngăn ngừa lỗi và tối ưu hóa mã
  • Thực tế, khi sử dụng OCaml trong nhiều dự án phát triển, tác giả đã thấy năng suấtđộ ổn định được cải thiện đáng kể

Ưu điểm của OCaml và ứng dụng trong thực tế

  • Phần lớn mã có thể được viết rất nhanh, và việc sử dụng kết hợp hàm cùng dữ liệu bất biến giúp tăng độ an toàn
  • Gần đây, hệ sinh tháicông cụ của OCaml (IDE, hệ thống build, v.v.) cũng tiếp tục phát triển
  • Nhờ nhiều thư viện và gói bên ngoài, việc phát triển hiệu quả trong môi trường thực tế trở nên khả thi hơn
  • So với Python hay Java, OCaml ít nổi tiếng hơn, nhưng là một lựa chọn rất mạnh về năng suất, an toàn và tính linh hoạt

Đặc tính ngôn ngữ

  • Sự kết hợp giữa nguồn gốc nghiên cứu và ứng dụng công nghiệp đã thúc đẩy các tính năng tập trung vào tính biểu đạtđộ an toàn
    • Các tính năng hiện đại như hiệu ứng do người dùng định nghĩa, affine session
  • Kiểm tra kiểu tĩnh vừa là lưới an toàn vừa là công cụ thiết kế, giúp xóa bỏ những hiểu lầm do trải nghiệm kiểu nghèo nàn gây ra
  • Đa mô hình: lập trình hàm, mệnh lệnh, mô-đun, hướng đối tượng, hỗ trợ đa lõi
  • Cú pháp họ ML ngắn gọn và nhất quán, đồng thời cũng có các cú pháp thay thế như ReasonML
  • Kiểu đại số (tích, tổng, mũ), pattern matching và tính đa hình mang lại thế mạnh trong mô hình hóa dữ liệu/lĩnh vực
  • Hệ thống mô-đun hỗ trợ tách biệt giao diện và triển khai, trừu tượng hóa, tái sử dụng, cho tới đa hình nâng cao
  • Đảo ngược phụ thuộc: cung cấp cách tiêm phụ thuộc linh hoạt thông qua mô-đun/hiệu ứng

Hệ sinh thái và tooling

  • Mục tiêu biên dịch: native, bytecode, JavaScript(Js_of_ocaml, Melange), WebAssembly
  • MirageOS mang lại quy tắc viết thư viện đa ngữ cảnh
  • OCaml Platform:
    • OPAM: quản lý phiên bản, switch, chỉ mục gói, hỗ trợ CI
    • Dune: build nhanh, cấu hình dạng S-expression, đơn giản hóa phát hành qua dune-release
    • LSP/Merlin: hoàn thành mã, điều hướng, định dạng trong VSCode, Emacs, v.v.
    • Odoc: hỗ trợ tham chiếu chéo, trang hướng dẫn thủ công, doctest, v.v.
  • Thư viện phong phú: web (Dream, Ocsigen), blockchain và mật mã (HACL*), kiểm thử (alcotest, qcheck, v.v.)
  • Thư viện chuẩn tuy nhỏ nhưng có các lựa chọn thay thế như Batteries, Base/Core, Containers

Thách thức mới và cộng đồng

  • Cộng đồng OCaml tuy nhỏ nhưng liên tục phát triển, đồng thời thể hiện xu hướng thân thiện với người dùng
  • Với những nhà phát triển muốn thử thách bản thân với ngôn ngữ hay mô hình mới, OCaml là lựa chọn rất đáng để học sâu
  • Nhiều người dùng cho biết trải nghiệm với OCaml giúp họ có được góc nhìn mớinăng lực giải quyết vấn đề tốt hơn

Kết luận

  • OCaml là một ngôn ngữ lập trình mạnh mẽ có thể được sử dụng rộng rãi, không chỉ giới hạn trong các lĩnh vực nhất định (ví dụ: tài chính, trình biên dịch, phát triển hệ thống)
  • Hiệu quả, khả năng bảo trì và năng lực phòng ngừa vấn đề đạt được trong thực chiến đã chứng minh giá trị của nó trong môi trường làm việc thực tế
  • Dù có thể kém nổi tiếng hơn so với các ngôn ngữ hay xu hướng mới, nếu coi trọng độ tin cậy và an toàn thì đây vẫn hoàn toàn là một lựa chọn đáng cân nhắc

2 bình luận

 
ng0301 2025-08-17

Tôi từng làm với OCaml ở bậc cao học, nhưng hệ sinh thái thực sự quá thiếu thốn, tài liệu tham khảo cũng không nhiều, và đặc biệt là hầu như không có ai để hỏi. Theo tiêu chí cá nhân của tôi, ở Hàn Quốc thì ngoài phía hội học thuật về ngôn ngữ lập trình ra gần như chẳng có ai dùng nó. Có thể người ta từng nghe đến COBOL rồi, nhưng OCaml thì có lẽ còn chưa nghe bao giờ..

 
GN⁺ 2025-08-15
Ý kiến Hacker News
  • Tôi từng xem một bài trình bày về kinh nghiệm đưa Rust vào đội Android tại Google. Có hai điểm làm tôi ấn tượng: họ đã chuyển nhiều dự án từ Python sang Rust, nên có lẽ hiệu năng không phải vấn đề lớn đến vậy; và tính năng mà người dùng Rust thích nhất lại là những thứ cơ bản như pattern matching và ADT (Algebraic Data Types). Vì thế tôi có cảm giác phần Rust thực sự đóng góp lớn không phải là những tính năng riêng như lifetime, mà là các yếu tố mà ngôn ngữ họ ML từ thập niên 1990 đã cung cấp từ lâu. Nếu OCaml giải quyết được những bất tiện như multicore vào khoảng năm 2010, có lẽ nó cũng đã nổi tiếng ngang Rust. Tiếc là OCaml rơi vào khoảng cách giữa giới học thuật và công nghiệp. Thêm một ý nữa là số nguyên 31-bit thực sự bất tiện khi làm bit operations, và về mặt thẩm mỹ thì tôi thật sự không ưa dấu chấm phẩy kép

    • Tôi nghĩ vào thời điểm đó OCaml đã ở trạng thái khá ổn rồi. Năm 2010 tôi dùng nó cho công việc và thấy dễ chịu hơn Python rất nhiều. Chỉ cần nhìn những gì JaneStreet đã làm được là thấy. Theo tôi, lý do lớn nhất khiến OCaml không được chấp nhận rộng rãi là vì nó không được tạo ra hay dẫn dắt tại Mỹ. Ta muốn tin rằng độ phổ biến của một ngôn ngữ đến từ ưu thế kỹ thuật, nhưng cuối cùng đây vẫn là vấn đề xu hướng. Rust cũng thành công đại chúng nhờ được quảng bá mạnh và có cộng đồng hoạt động rất tích cực. Họ thậm chí còn có cả nhân sự chuyên trách để quảng bá tên tuổi

    • Google cố gắng giữ danh sách ngôn ngữ chính thức được phép dùng cho code dịch vụ thực tế càng ngắn càng tốt. Có vẻ Rust được chọn vì nó là ngôn ngữ có thể thay thế hoặc bổ sung cho C++. OCaml khó mà ở vào vị trí đó được (có thể thay Go, nhưng khả năng thấp). Vì vậy lý do lớn nhất Rust được chọn có lẽ là vì nó là ngôn ngữ chính thức duy nhất cung cấp ADT, chứ không phải vì người ta không coi trọng tốc độ build. Việc OCaml không thể thay Rust cũng là điều hiển nhiên. Các ngôn ngữ có GC như Go, Haskell... vốn đã có nhiều, còn vào khoảng năm 2010 thì ngôn ngữ đủ giàu sức biểu đạt để nhắm tới bare metal gần như chỉ có C++ mà thôi (mà ngay cả C++ trước C++11/C++17 còn tệ hơn nữa)

    • Hoàn toàn đồng ý. Nếu OCaml sửa được vài vấn đề lặt vặt thì nó thực sự có thể trở thành một tay chơi quan trọng. Tốc độ build đến giờ vẫn nhanh hơn Rust rất nhiều. Nhưng OPAM (trình quản lý gói) thì hay lỗi và nổi tiếng là khó hiểu. Hỗ trợ Windows thì tệ đến mức nghiêm trọng, còn hơn cả hỗ trợ Windows của Perl ngày trước. Tài liệu chính thức thì quá ngắn gọn đến mức gần như vô dụng. Bản thân cú pháp cũng khó nắm bắt, và chỉ một lỗi gõ nhỏ cũng thường khiến thông báo báo rằng nửa file là lỗi cú pháp. Cú pháp kiểu C hiện có của Rust dễ tiếp cận hơn nhiều. Tóm lại, điểm mạnh của OCaml chỉ là build nhanh, mà như thế thì chưa đủ lý do để phải dùng nó

    • Vì vậy khi tôi muốn lập trình theo phong cách ML, tôi thường tìm đến Kotlin, Scala, hoặc F# trước Rust. Và giờ đây ngay cả Java hay C# cũng đã tiếp thu đủ nhiều yếu tố ML để tôi không còn thấy khó chịu. Tôi đã quen với hệ kiểu ML từ thời Caml Light và Objective Caml, nên khi thấy mọi người ngày nay phát cuồng vì Rust, tôi có cảm giác như họ đang lầm tưởng Rust là thứ mới mang hệ kiểu ML đến

    • Về ý kiến rằng OCaml lẽ ra nên chuẩn bị tốt hơn, tôi thực ra nghĩ ưu điểm lớn nhất là việc có nhiều lựa chọn ngôn ngữ. Chỉ riêng Vương quốc Anh thôi, dù dân số ít hơn, cũng có sự cùng tồn tại của rất nhiều ngôn ngữ. Ví dụ, tiếng Cornish từng là ngôn ngữ chết ở châu Âu nhưng gần đây đã được cư dân địa phương hồi sinh, và trong giới chăn cừu vẫn còn tồn tại một ngôn ngữ đếm số gọi là Kubrik. Bản thân tôi cũng bắt đầu dùng một chương trình tên Geneweb dựa trên OCAML cho phả hệ của thế hệ sau (chuyển từ ứng dụng Windows tên TMG). Nó chứa dữ liệu gia đình của 140 nghìn người. Việc Geneweb được viết bằng OCAML khiến tôi quan tâm hơn đến ngôn ngữ này. Nếu bạn thấy ngôn ngữ lập trình là khó, tôi khuyên hãy thử làm gia phả/điền dã phả hệ. Chẳng bao lâu sau bạn sẽ đau đầu vì chuẩn GEDCOM

  • OCaml là một trong những ngôn ngữ tôi yêu thích nhất. Dự án tôi làm nhiều nhất là triển khai một ứng dụng CRUD 100% cho ban tổ chức Writer's Festival bằng OCaml (JSX dựa trên ReasonML), Dream, HTMX và DataTables. Tôi tái sử dụng template frontend bằng module, và mỗi khi có thay đổi trong data model thì compiler lập tức chỉ ra chỗ nào bị vỡ, điều đó làm tôi rất hài lòng. Tôi cũng đã chuyển dữ liệu Excel sang DB đúng nghĩa, và triển khai được khá nhiều thứ trong hệ sinh thái OCaml, như xuất lịch biểu mẫu định dạng .odt hoặc xuất trực tiếp thành file zip mà không cần đi qua đĩa của server. Tuy vậy, việc phải viết toàn bộ truy vấn DB dưới dạng string, rồi chuyển kiểu cũng làm thủ công, khiến tôi mệt kinh khủng (không có type check ở compile time). Hệ thống xác thực cũng phải tự tay làm, nên tôi thường tốn quá nhiều thời gian vào những việc không phải phát triển sản phẩm cốt lõi. Sau khi nhìn quanh nhiều ngôn ngữ, điều tôi rút ra là không tồn tại ngôn ngữ hoàn hảo. Mỗi ngôn ngữ đều có nhược điểm rất riêng. Hiện tại tôi đang làm app cho riêng mình bằng Rails, và hài lòng hơn nhiều vì gần như mọi thứ cần thiết đều có sẵn theo mặc định, nên tôi có thể tập trung vào những việc đúng bản chất hơn như thiết kế layout thực tế hay triển khai thật, thay vì bận tâm về ngôn ngữ

    • Tôi tò mò không biết trong các ngôn ngữ hàm có hệ kiểu mạnh thì cách xử lý kết quả DB theo kiểu idiomatic là như thế nào
  • DarkLang ban đầu được phát triển bằng OCaml, sau đó chuyển sang F#. Lý do chính là hệ sinh thái thư viện và concurrency (bài liên quan). Tôi có thể hơi thiên vị vì đã quen với .NET, nhưng ở đó ngay cả những phần nhàm chán cũng đã có nhiều lựa chọn ổn để bạn tập trung vào vấn đề cốt lõi. Tôi đã dùng F# trong công việc khá nhiều và còn duy trì một thư viện UI phổ biến, nhưng vì hệ sinh thái ngôn ngữ nhỏ nên ngay cả trong .NET, giải pháp cũng không phải lúc nào cũng có sẵn ngay. Vì vậy cần nhớ rằng chọn một ngôn ngữ ngoài dòng chính (ví dụ F# thay vì C#) sẽ có cái giá của nó. OCaml cũng vậy: nó cung cấp một ngôn ngữ mạnh, nhưng vì đi chệch khỏi dòng chính nên có nhiều bất tiện. Dù một vài công ty có dùng nó trong dịch vụ thực tế, đó vẫn là những trường hợp phù hợp với nhu cầu rất đặc thù của họ

  • Tôi đã cố gắng thích OCaml trong vài năm, nhưng phần khó chịu nhất là “không thể print một object bất kỳ”. Có thể dùng ppx để tự động sinh hàm to_string, nhưng việc cấu hình phiền phức và trải nghiệm kém hơn Rust. Muốn in các kiểu như Set, Map... cũng cần làm thêm bước nữa (ví dụ tham khảo). Trong golang, bạn có thể dễ dàng in gần như mọi thứ bằng format "%v", còn OCaml thì ở điểm này vẫn phải động tay nhiều hơn

    • Format %v của Go cũng không hoàn hảo, và nếu muốn traverse sâu hơn qua con trỏ thì vẫn cần thư viện riêng như go-spew. Cách __repr__ của Python là thứ tiện nhất tôi từng thấy cho đến nay
  • Tôi chưa trực tiếp dùng OCaml, nhưng trải nghiệm làm việc với F# rất dễ chịu. Trong thời đại LLM hiện nay, tôi nghĩ cũng nên nhìn lại các ngôn ngữ hàm. Trong các mô hình như OCaml hay Haskell, vì có thể nén thông tin vào lượng văn bản nhỏ hiệu quả hơn, biết đâu lại chứa được nhiều ý nghĩa hơn trong context window của LLM. So với Java, C#, Ruby, có lẽ còn có thể thử áp dụng những thay đổi phức tạp hơn chỉ trong một lần

    • Ban đầu tôi cũng nghĩ vậy, nhưng sau khi thật sự làm việc trong codebase Haskell lớn, tôi đã đổi ý. Có lẽ vì tập dữ liệu huấn luyện thiếu FP, nên ngôn ngữ càng cô đọng lại càng không hợp với LLM. Tôi có cảm giác code càng verbose thì LLM càng có nhiều cơ hội tự sửa sau khi dự đoán sai token, từ đó sinh ra code đúng hơn

    • Trong thử nghiệm cá nhân, tôi đã làm một game CLI đơn giản bằng C++ và Haskell. Số dòng của Haskell ít hơn, nhưng số từ gần như tương đương, nên chỉ là code trông “rộng” hơn thôi. Tôi chưa so với Java hay các ngôn ngữ tường minh hơn, nhưng tôi nghĩ phong cách phù hợp còn phụ thuộc vào tính chất chương trình. Có thứ hợp với kiểu mệnh lệnh, có thứ hợp với kiểu hàm hơn

    • Nếu khả năng sinh code của LLM tiến bộ thêm một chút nữa, tôi thật sự mong có thể dùng hệ kiểu cực mạnh cùng effect system để ràng buộc phạm vi hành vi của code. Ví dụ, nếu có dependent types thì có thể kiểm tra ở compile time những điều kiện như “hàm này chắc chắn trả về một danh sách đã được sắp xếp” hoặc “hàm này chắc chắn trả về một lời giải Sudoku hợp lệ”. Nếu thêm effect system vào, còn có thể chỉ định “hàm này trả về lời giải Sudoku hợp lệ nhưng không truy cập network hay filesystem”. Khi LLM tiến hóa hơn, có lẽ nó vẫn làm được mức này ngay cả trong Python, nhưng nếu tiến bộ chậm, tôi nghĩ tương lai sẽ là dùng LLM kém tin cậy được bọc trong các hệ thống quyết định có độ tin cậy cao

    • Khi dùng cats-effect trong Scala, tốc độ phát triển của tôi tăng lên rất nhiều nhờ LLM. Code cats-effect thường khiến cả khái niệm đơn giản cũng trở nên khó nhằn, nhưng chỉ cần hỏi LLM kiểu “làm việc ~ trong cats-effect như thế nào?” là 80% trường hợp được giải quyết ngay. 20% còn lại chỉ cần thêm ngữ cảnh. Về mặt bảo trì thì tôi vẫn đang thử nghiệm, nhưng mức độ bế tắc khi làm functional programming dựa trên effect đã giảm đi rất nhiều. Lần tới tôi muốn thử xem Claude Code làm tốt đến đâu

    • Haskell có hai lợi thế lớn với việc sinh code bằng LLM. Thứ nhất, hệ kiểu giàu tính biểu đạt giúp bắt được nhiều lỗi, và bản thân các lỗi compile đó có thể đưa lại làm feedback cho LLM. Thứ hai, rất dễ cải thiện code hiệu quả và chính xác nhờ property-based testing (QuickCheck, v.v.). Bản thân LLM không viết test quá giỏi, nhưng nếu bạn tự thêm vào thì nó giúp bắt lỗi trong code sinh ra rất nhanh

  • Đọc bài này xong tôi thấy như đã chấm dứt hẳn câu hỏi “tại sao không dùng F# thay vì OCaml?”. Gần như trong mọi thread về OCaml đều có người gợi ý “dùng F# thì chẳng phải giải quyết xong vấn đề tooling sao?”. Tôi cũng từng tò mò về OCaml và thích biệt danh “Go with types”, nhưng bản thân OCaml vẫn chưa thật sự trở nên hấp dẫn hoàn toàn với tôi. Nó có gì đó khác với sự nhiệt huyết ở cộng đồng Erlang, Ruby, Rust, Zig và các ngôn ngữ khác

    • Ngược lại, tôi lại là người chuyển sang OCaml để tránh hệ sinh thái công cụ của F#. Khi tôi dùng F#, compiler chậm, hệ sinh thái thì tập trung vào C#, MSBuild vừa yếu vừa thiếu tài liệu, Ionide thì hay crash, Fantomas cũng không đáng tin cậy — tooling có rất nhiều vấn đề. Tuy vậy, OCaml cũng chưa thể thay thế hết các tính năng hướng hiệu năng của F# (ví dụ value types và những phần CLR hỗ trợ). Vì thế đến giờ tôi vẫn chưa tìm được một ngôn ngữ họ ML đơn giản đúng ý mình. Tôi đang hy vọng tương lai OxCaml hay thứ tương tự có thể giải quyết điều đó

    • Gần đây tôi không dùng OCaml nhiều, nhưng bản thân phần cốt lõi của ngôn ngữ này vẫn là thứ tôi thích nhất. Phong cách viết code của tôi có xu hướng dồn vào một hàm lớn, còn trong OCaml thì tôi tự nhiên tránh được điều đó. Tôi có dùng Rust cho side project, nhưng thật ra OCaml vẫn thấy thoải mái hơn. Vì những lý do trên, tôi cũng rất muốn thử F# một lần

  • Tôi có câu hỏi về thuật ngữ: trong bài, kiểu hàm được gọi là “kiểu mũ” (exponential types), nhưng tôi chưa hiểu tại sao kiểu hàm bậc cao lại được gọi như vậy

    • Đã có nhiều giải thích hay rồi, nhưng lý do sâu hơn là kiểu hàm tuân theo các quy luật số mũ về mặt đại số. Ví dụ A → (B → C) đẳng cấu với (A × B) → C thông qua currying. Điều này tương tự với (cᵇ)ᵃ = cᵇ˙ᵃ. Và (A + B) → C đẳng cấu với (A → C) × (B → C), tương ứng với quy tắc cᵃ⁺ᵇ = cᵃ·cᵇ

    • Ngay cả kiểu hàm bậc một cũng đã mang tính số mũ. Ví dụ, sum type có số lượng giá trị bằng tổng số case. (Ví dụ: A of bool | B of bool2+2=4 trường hợp). Product type và exponential type cũng vậy. Nếu viết bool -> bool thì có 2^2 = 4 giá trị khả dĩ (nếu không xét side effects)

    • Khi nói về ADT (Algebraic Data Type), người ta thường chỉ nói đến sum và product. Hàm không phải dữ liệu nên ít được nhắc đến. Nhưng kiểu a -> bb^a trường hợp, nên có thể tiếp cận theo cùng cách

    • Tôi cũng từng có cùng thắc mắc, nhưng về mặt toán học thì sau tổng (sum) và tích (product) là lũy thừa (exponent), nên có lẽ người ta gọi như vậy theo phép so sánh

    • Các câu trả lời đều đúng, nhưng thực ra trong category theory, kiểu hàm được gọi là “exponential product”. Tên đó cũng bắt nguồn từ việc số lượng hàm từ A sang B được tính bằng lực lượng của B mũ lực lượng của A

  • Sum types: Ví dụ, Kotlin, Java và C# có thể dùng sealing (niêm phong quan hệ kế thừa) để xem từng case như type riêng của nó
    lập luận rằng khai báo sum type quá dài dòng nên khó hiểu
    sum type thường chỉ khai báo một lần nhưng dùng nhiều lần. Nếu các case khi sử dụng trông gọn gàng hơn, thì một chút dài dòng ở chỗ khai báo là hoàn toàn chấp nhận được

    • Các case của sum type là giá trị (expression) thông qua type constructor, nên đương nhiên chúng có type. Ví dụ,

      datatype shape =
         Circle of real
        | Rectangle of real * real
        | Point
      

      mỗi case đều được gán type. Nhờ pattern matching, các tham số của type constructor đã được unpack trực tiếp rồi. Nếu tách case ra thành type riêng, bạn sẽ đánh mất lợi ích exhaustiveness (tránh bỏ sót) vốn có của sum type, và thậm chí còn biểu diễn được những trạng thái chương trình sai. Sum type chỉ cần khai báo một lần rồi dùng nhiều lần, và thường mang tính disposable. Khả năng đọc code cũng quan trọng, nên đôi khi sự dài dòng bị đánh giá quá thấp. Nhân tiện thì C#/Java không thật sự hỗ trợ sum type. Nhìn ví dụ dưới đây sẽ thấy C# phức tạp không cần thiết vì cách tiếp cận OOP:

      public abstract record Shape;
      public sealed record Circle(double Radius) : Shape;
      ...
      double Area(Shape shape) => shape switch {...};
      

      Trong ML thì xử lý gọn hơn nhiều

      datatype shape = Circle of real | Rectangle of ... | Point
      val result = case shape of ...
      

      Hai cách gần như giống nhau, nhưng các yếu tố OOP của C# lại trở thành vật cản

    • Trong OCaml, cũng có thể dùng GADT, Polymorphic Variants, v.v. để dùng chúng gần như type riêng biệt. Nhưng nhìn chung, tách sum type ra thường khiến việc tổng quát hóa khó hơn và hiểu cũng khó hơn. Nó còn kéo theo vấn đề về type equality và variance

    • Tôi không hiểu vì sao cứ phải tranh luận sum-type với sealed-type. Tôi thích ngôn ngữ hàm hơn, nhưng nhờ tính phân biệt ở cấp type, chỉ với sealed type cũng có thể mô hình hóa toàn bộ sum type, và nhờ subtyping mà có những mặt việc định nghĩa và sử dụng lại dễ hơn. Paradigm của hệ thống thì khác nhiều, nhưng về mặt toán học gần như tương đương, và các trò vặn vẹo type trong OOP hay FP hầu như đều có thể hiện thực trong phạm vi ngôn ngữ cho phép

    • Tôi không đồng ý rằng sự dài dòng trong khai báo sum type của Java/Kotlin là cái giá đáng trả. Nó cho tôi cảm giác đúng kiểu boilerplate đặc trưng của ngôn ngữ JVM

  • Tôi muốn có ai đó hiểu đủ sâu cú pháp ReasonML để so sánh ưu nhược điểm. (Bài chỉ nhắc thoáng qua thôi)

    • Điều tôi tiếc nhất là let binding (tài liệu chính thức). Trong ReasonML, có thể tự custom các toán tử như >>= cho monad nên rất dễ dùng. rescript (fork của ReasonML) thì vẫn chưa có cái đó. Bù lại, nó hỗ trợ cú pháp async/await tốt nên hữu ích cho code bất đồng bộ. Melange (được nhắc ngắn trong bài) hỗ trợ let binding trong cú pháp Reason. Vì vậy với frontend dùng React, Reason ML của Melange có lợi thế rất lớn. Nhờ let binding (cùng JSX), có thể viết code async theo kiểu monadic rất gọn. Với cú pháp OCaml, có thể lách bằng PPX nhưng editor highlight không tốt lắm. Nếu nhìn từ backend, tôi thích phong cách Python nên vẫn thấy dấu ngoặc nhọn hơi khó chịu, và cũng thích cách gọi/định nghĩa hàm không cần ngoặc hơn. Nhưng với tư cách người mới dùng OCaml, tôi vẫn thấy việc dùng dấu ngoặc với các đối số không phải biến khá rối. Hy vọng trải nghiệm này hữu ích

    • Tôi không dùng ReasonML nhiều nên cũng không cảm được ưu điểm của nó. Ngoài chuyện nó đã chết tới hai lần trong 4 năm...

    • Tôi cũng muốn cú pháp Reason được phổ biến hơn, nhưng nếu muốn giao tiếp với cộng đồng OCaml thì tốt hơn là cứ học cú pháp chuẩn. Phần lớn code và tài liệu đều dùng cú pháp chuẩn, nên trước sau gì bạn cũng phải biết

    • Điều bất tiện nhất với ReasonML theo trải nghiệm của tôi là LSP không hoạt động tử tế

  • Tôi muốn được giải thích kỹ hơn phần triển khai dependency injection bằng effects system. Ý tưởng bind giá trị test/production bằng pattern matching nghe có vẻ thú vị nhưng chỉ đọc thì chưa hình dung ra. Và tôi cũng mới biết module system lại có cả hệ kiểu riêng, khá bất ngờ

    • Tôi là một Haskeller!

      Về câu hỏi chưa hình dung được việc bind giá trị test/production thông qua pattern matching trong ví dụ... Điều bài viết đang nói tới là mẫu free monad + interpreter: thay vì trực tiếp thực thi action trong business logic, bạn biến các thao tác thành danh từ rồi tích lũy thành AST. Khi đã có AST, có thể tách việc thực thi thật và test bằng ProdAstVisitor, TestAstVisitor. Cụ thể hơn, ý là ở mỗi node của AST sẽ chọn Test.ReadFile/WriteFile, chứ không phải trộn trực tiếp Test và Prod với nhau. Cộng đồng Haskell hiện đang hơi rời xa mẫu free monad + interpreter để chuyển sang cách tiếp cận tagless final, vì ít ceremony hơn mà hiệu quả tương tự. Về ý muốn xem thêm cách làm DI bằng effects system... Tôi có làm DI bằng effects. Cách tôi thấy ổn là như sau: Ở tầng thấp nhất, đặt các capability interface tách riêng theo chức năng như readTextFile, writeTextFile, v.v. Những class này tuyệt đối không nên biết gì về tri thức nghiệp vụ (domain). Ở tầng trên, kết hợp nghiệp vụ với capability để triển khai các business function như fetchStoredCommands. Ở tầng trên cùng, có một type là CliApp, và ở đây sẽ bind với implementation thật của các capability interface. Ví dụ, nếu nối riêng Logger sang một phiên bản Mock, thì việc tách môi trường production/test sẽ được triển khai bằng typeclass. Điểm quan trọng là toàn bộ hàm đều khai báo rõ effect type trong type system (có phải IO hay không, v.v.). Implementation production thật thì được phép IO, còn implementation test thì không được IO. Nếu bạn tạo thêm capability mới (ví dụ Caching) mà quên implementation test, lỗi sẽ bị bắt ở compile time, nên có độ an toàn rất cao. Tôi cũng tò mò không biết trong OCaml có thể làm theo cách tương tự hay không. Bài viết đó nói rằng hiện tại việc lan truyền effect vẫn chưa được type system theo dõi