2015-12-21 10 views
6

In Haskell, le monadi sono definite in termini di funzioni return e bind, dove return ha type a -> m a e bind ha tipo m a -> (a -> m b) -> m b. È stato sottolineato prima che lo monads can also be defined in terms of return and join, dove join è una funzione con tipo m (m a) -> m a. Il binding può essere definito in termini di join, ma è possibile il contrario? Unirsi può essere definito in termini di vincolo?Con monadi, è possibile unire un join in termini di bind?

Senza join, non ho idea di cosa farei se trovassi un valore monadico "doppio avvolto", m (m a) - nessuna delle operazioni del functor o monad "rimuove alcun livello", per così dire. Se questo non è possibile, perché Haskell e molte altre implementazioni di monad li definiscono in termini di legatura? Sembra strettamente meno utile di una definizione basata su join.

+5

Nota "anche": se aderire non poteva essere definito in termini di vicolo cieco, sarebbe _deve_ essere un membro di 'Monad' (o non sarebbe definito per tutte le monadi), e non lo è. –

risposta

9

E 'possibile:

join :: Monad m => m (m a) -> m a 
join m = (m >>= id) 

Nota l'istanza ingannevole di >>=:

(>>=) :: m b -> (b -> m c) -> m c 
-- choosing b ~ m a , c ~ a 
(>>=) :: m (m a) -> (m a -> m a) -> m a 

così possiamo scegliere correttamente id per la seconda discussione.

6

Sì, è abbastanza semplice:

join m = m >>= id 
3

Bind (>>=) in effetti "rimuovere un livello":

(>>=) :: Monad m => m a -> (a -> m b) -> m b 

Intuitivamente "ottiene alcuni a s fuori dal m a", e alimenta quindi alla funzione a -> m b, e quindi produce un unico m b dai risultati.

Le persone di solito dicono che richiede l'argomento della funzione per riassociare nuovamente l'output in m, ma in realtà non è così. Richiede l'output della funzione su su qualcosa avvolto in m, ma non importa da dove viene il wrapping.

In caso di implementazione di join stiamo iniziando da qualcosa "double-wrapped": m (m a). Siamo in grado di collegare che nella firma per legare e immediatamente capire il tipo di funzione che potremmo usare quando si associa un valore di "doppio-avvolto":

m (m a) -> (m a -> m b) -> m b 

Ora la funzione utilizzata con bind qui sta per ricevere un valore quello è già avvolto in m. Quindi non dobbiamo "re-avvolgere" nulla; se lo restituiamo non modificato sarà già il tipo giusto per l'output. In effetti questo è "rimosso uno strato di avvolgimento" - e questo funziona per qualsiasi livello ma l'ultimo.

In modo che ci dice non ci resta che legare con id:

join = (>>= id)