2017-09-07 16 views
7

Ví dụ, MaybeT được định nghĩa là:Trong một biến áp đơn nguyên, tại sao đơn vị được biết đến là cái bên trong?

newtype MaybeT m a = 
    MaybeT { runMaybeT :: m (Maybe a)} 

Nhưng không:

newtype MaybeT m a = 
    MaybeT { runMaybeT :: Maybe (m a) } 

Tại sao điều này?

+1

Bởi vì chúng tôi chuyển đổi đơn nguyên 'Có thể', chứ không phải cái' Có thể' chứa? –

+0

Tôi nghĩ rằng điều này đã được trả lời ở đâu đó trước đây, nhưng tôi không thể tìm thấy nó. – leftaroundabout

Trả lời

6

Sau khi mở rộng kiểu mới, chúng tôi có join :: Monad m => m (Maybe (m (Maybe a))) -> m (Maybe a) trong trường hợp đầu tiên và join :: Monad m => Maybe (m (Maybe (m a))) -> Maybe (m a) trong giây.

Để thực hiện join đầu tiên bạn cần một cách để phân phối Maybe qua m: dist1 :: Monad m => Maybe (m a) -> m (Maybe a):

join1 :: m (Maybe (m (Maybe a))) -> m (Maybe a) 
join1 = fmap join . join . fmap dist1 

Để thực hiện thứ hai join bạn cần luật phân phối đối diện dist2 :: Monad m => m (Maybe a) -> Maybe (m a)

join2 :: Maybe (m (Maybe (m a))) -> Maybe (m a) 
join2 = fmap join . join . fmap dist2 

dist1 rất dễ dàng để thực hiện (tôi sẽ để lại việc chứng minh luật biến áp đơn lẻ cho bạn):

dist1 :: Monad m => Maybe (m a) -> m (Maybe a) 
dist1 = sequenceA 

dist2 không dễ dàng như vậy. Nó không thể được thực hiện cho một tùy ý Monad. Như một phản ví dụ, chúng ta hãy chọn m là "đọc" đơn nguyên (->) r:

dist2 :: (r -> Maybe a) -> Maybe (r -> a) 

Vì bạn không có quyền truy cập vào một r, việc thực hiện duy nhất của dist2 rằng sẽ typecheck là const Nothing, mà thắng rõ ràng' t thỏa mãn luật đơn nguyên.

+1

Tôi nghĩ rằng dễ dàng hơn để xem vấn đề bằng cách tập trung vào 'join2' trực tiếp, thay vì cố gắng lý do quá sâu sắc về cách nó phải phân hủy. Chúng ta có thể khớp mẫu trên đối số, và sau đó chúng ta cần 'm (Có thể (m a)) -> Có thể (m a)'. Sự lựa chọn không tầm thường duy nhất là 'Just', dẫn đến' m (Có thể (m a)) -> m a', có mùi giống như rắc rối. – dfeuer

2

Nhìn vào StateT có thể là bài học:

newtype StateT s m a = StateT { runStateT :: s -> m (a,s) } 

Ở đây nhà nước không phải là "nội" hay "bên ngoài", nhưng kiểu của nó là xen kẽ với các đơn nguyên nó đang thay đổi, một số bit bên trong, một số bên ngoài. Và thực sự là

newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a } 

là tất cả "bên ngoài". Vì vậy, nó phụ thuộc vào những gì biến nó được. Có lẽ một số lý thuyết thể loại mà ít nhất một phần giải thích xen kẽ này, tôi tò mò muốn biết về nó (trí thức?).

Contrast với functors applicative, mà

newtype Compose f g a = Compose { getCompose :: f (g a) } 

là một applicative là tốt, vì vậy luôn luôn có một rõ ràng "bên trong/bên ngoài" mối quan hệ. Bạn có thể thực hiện một applicative chỉ StateT, và tìm ra cấu trúc của nó bởi Compose (State s):

ApplicativeStateT s f a = s -> (s, f a) 

Trong thực tế, có một số khác nếu bạn soạn ở bên phải:

ApplicativeStateT' s f a = f (s -> (s,a)) 

Nhưng monads không có đều đặn như vậy.

+1

Tôi nghi ngờ tên chúng tôi cung cấp cho một số máy biến áp có thể đề xuất một mẫu giả. Chúng tôi đặt tên 'StateT' sau' State' và 'ReaderT' sau' Reader', v.v., dường như gợi ý mối quan hệ một-một của một loại. Nhưng các máy biến áp đơn điệu và monad thực sự khá khác nhau, được chứng minh bằng những thứ khác nhau mà xưng là "biến thể" danh sách đơn vị "đúng". "' ListT' được thực hiện đúng ", ống/ống dẫn/streaming/máy/coroutines, và' LogicT' (và phiên bản phản chiếu-không-hối hận của 'LogicT') tất cả dường như có những tuyên bố hợp pháp đối với tiêu đề. – dfeuer

Các vấn đề liên quan