9 điểm bởi bboydart91 2026-02-08 | 1 bình luận | Chia sẻ qua WhatsApp

Nội dung

  • Giải thích bằng mã TypeScript quá trình đi từ hai vấn đề mà chỉ map của functor không thể giải quyết được (vấn đề hàm bị mắc kẹt trong container và vấn đề lồng ngữ cảnh khi hợp thành) đến applicative functor và monad
  • Bắt đầu từ bối cảnh năm 1988 khi Eugenio Moggi mô hình hóa chương trình thành A → T(B) thay vì A → B
  • Trình bày cấu trúc flatMap = map + join và ba luật để sử dụng an toàn cấu trúc này (kết hợp, đơn vị trái, đơn vị phải)
  • Giải thích vì sao monad là "đối tượng monoid trong phạm trù endofunctor" bằng cách đối chiếu với monoid của phép cộng số nguyên
  • Cũng đề cập lý do Promise hoạt động theo kiểu monadic nhưng không phải là monad toán học theo nghĩa chặt chẽ

Giới hạn của functor: những gì map không làm được

  • Khi áp dụng một hàm đã được curry bằng map, kết quả là Maybe<(b: number) => number> nên hàm bị mắc kẹt trong container
    • map chỉ có thể nhận hàm ở bên ngoài container, nên không có cách nào áp dụng hàm bị nhốt bên trong cho một giá trị khác
  • Khi hợp thành hai hàm trả về functor, ngữ cảnh sẽ bị lồng như Maybe<Maybe>
    • Càng nhiều bước thì càng lồng vô hạn thành Maybe<Maybe<Maybe<...>>>

Applicative functor: áp dụng hàm bên trong container

  • Với phép toán apply, có thể áp dụng một hàm bị giữ trong container lên giá trị của container khác
    • apply: T<(A → B)> → T<A> → T<B>
  • Với phép toán pure, chèn một giá trị thuần vào container
  • Giới hạn: phải xác định trước sẽ hợp thành những container nào
    • Không thể biểu diễn sự phụ thuộc tuần tự động, tức quyết định phép tính tiếp theo dựa trên kết quả của phép tính trước

Monad: phát minh ra phép toán làm phẳng lồng nhau

  • Phép toán join biến T<T<A>> → T<A>, làm phẳng container kép thành một lớp duy nhất
    • Array.prototype.flat của JavaScript đóng cùng vai trò
  • Trong thực tế, người ta dùng flatMap, là sự kết hợp của map + join
    • flatMap: T<A> → (A → T<B>) → T<B>
    • map nhận A → B, còn flatMap nhận A → T<B> để giữ kết quả ở một lớp duy nhất

Ba luật của flatMap

  • Luật kết hợp: khi làm phẳng ba lớp lồng nhau T(T(T(A))), dù làm phẳng từ trong ra ngoài
    hay từ ngoài vào trong thì kết quả cũng phải giống nhau
    • m.flatMap(f).flatMap(g) === m.flatMap(x => f(x).flatMap(g))
  • Luật đơn vị trái: nếu đưa vào bằng pure rồi flatMap ngay, thì tương đương với việc áp dụng trực tiếp hàm đó
    • pure(a).flatMap(f) === f(a)
  • Luật đơn vị phải: nếu truyền pure vào flatMap, ta nhận lại đúng container ban đầu
    • m.flatMap(pure) === m

Phân tích cụm từ "đối tượng monoid trong phạm trù endofunctor"

  • Functor trong lập trình đi từ thế giới kiểu dữ liệu đến chính thế giới kiểu dữ liệu, nên là endofunctor
  • Có thể xây dựng một phạm trù endofunctor, trong đó chính các endofunctor là đối tượng
  • Nếu thay các điều kiện của monoid (phép toán nhị phân + luật kết hợp + phần tử đơn vị) vào thì sẽ có:
    • phép toán nhị phân = join
    • phần tử đơn vị = pure
    • Cấu trúc này tương ứng chính xác với monoid của phép cộng số nguyên

Vì sao Promise không phải là monad

  • then xử lý lẫn lộn mapflatMap tùy theo giá trị trả về
  • Trạng thái Promise<Promise> không được cho phép ở runtime và lập tức được gộp thành một
    lớp duy nhất
  • Dù tiện lợi trong thực tế, nó không thỏa các luật monad theo nghĩa toán học

1 bình luận

 
calofmijuck 2026-02-08

Hãy đề cập cả Comonad nữa!