Nếu chúng ta so sánh các loại
(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m => m s -> (s -> m t) -> m t
chúng tôi nhận được manh mối về những gì phân tách hai khái niệm. Điều đó (s -> m t)
trong loại (>>=)
cho thấy rằng một giá trị trong s
có thể xác định hành vi của một phép tính trong m t
. Đơn nguyên cho phép giao thoa giữa các lớp giá trị và tính toán. Các (<*>)
nhà điều hành cho phép không có sự can thiệp như: chức năng và lập luận tính toán không phụ thuộc vào giá trị. Điều này thực sự cắn. Đối chiếu
miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
b <- mb
if b then mt else mf
trong đó sử dụng kết quả của một số hiệu ứng để quyết định giữa hai phép tính (ví dụ: phóng tên lửa và ký hiệp định đình chiến), trong khi
iffy :: Applicative a => a Bool -> a x -> a x -> a x
iffy ab at af = pure cond <*> ab <*> at <*> af where
cond b t f = if b then t else f
trong đó sử dụng giá trị của ab
để lựa chọn giữa các giá trị của hai phép tính at
và af
, đã thực hiện cả hai, có lẽ gây ra hậu quả bi thảm.
Phiên bản đơn nguyên về cơ bản dựa vào sức mạnh bổ sung của (>>=)
việc chọn phép tính từ một giá trị và điều đó có thể quan trọng. Tuy nhiên, việc hỗ trợ sức mạnh đó khiến các monads khó có thể soạn thảo. Nếu chúng tôi cố gắng xây dựng 'liên kết kép'
(>>>>==) :: (Monad m, Monad n) => m (n s) -> (s -> m (n t)) -> m (n t)
mns >>>>== f = mns >>-{-m-} \ ns -> let nmnt = ns >>= (return . f) in ???
chúng ta đi được xa đến mức này, nhưng bây giờ các lớp của chúng ta đều bị lộn xộn. Chúng ta có một n (m (n t))
, vì vậy chúng ta cần phải loại bỏ cái bên ngoài n
. Như Alexandre C nói, chúng ta có thể làm điều đó nếu chúng ta có
swap :: n (m t) -> m (n t)
để hoán vị phần n
trong và phần bên trong join
với phần kia n
.
'Áp dụng kép' yếu hơn dễ xác định hơn nhiều
(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs
vì không có sự giao thoa giữa các lớp.
Tương ứng, thật tốt để nhận ra khi nào bạn thực sự cần thêm sức mạnh của Monad
s và khi nào bạn có thể thoát khỏi cấu trúc tính toán cứng nhắc Applicative
hỗ trợ.
Nhân tiện, xin lưu ý rằng mặc dù việc soạn thảo các monads rất khó, nhưng nó có thể nhiều hơn mức bạn cần. Kiểu m (n v)
chỉ ra tính toán với m
-effects, sau đó tính toán với n
-effects thành -value v
, trong đó m
-effects kết thúc trước khi n
-effects bắt đầu (do đó cần swap
). Nếu bạn chỉ muốn xen kẽ m
-effects với n
-effects, thì bố cục có lẽ là quá nhiều để yêu cầu!