2013-05-06 12 views
6

Ha senso definire più metodi flatMap (o >>=/bind in Haskell) in una Monade? Le pochissime monadi che uso effettivamente (Option, Try, proiezioni) definiscono solo un metodo flatMap.Metodi di flatMap multipli per una singola monade?

ad esempio, può aver senso definire un metodo flatMap su Option che potrebbe assumere una funzione producendo un Try? In modo che Option[Try[User]] venga appiattito come Option[User] per esempio? (Considerando perdere l'eccezione non è un problema ...)

Oppure monade deve solo definire un metodo flatMap, prendendo una funzione che produce lo stesso tipo di monad? Immagino che in questo caso le proiezioni Either non sarebbero monade? Sono loro?

+0

@ om-nom-nom e La lista è una Monade allora? Tra l'altro non riesco a trovare il modo in cui funziona per 'List [Option [_]]' poiché Option non è un GenTraversableOnce –

+0

'Oither' è una monade su entrambe le sue variabili di tipo. E 'questo quello che stai chiedendo? –

risposta

5

Ho pensato seriamente a questo. Come si è visto, una tale costrutto (oltre a perdere tutte le funzionalità monadici) non è molto interessante, in quanto è sufficiente a fornire una conversione dal interna al contenitore esterno:

joinWith :: (Functor m, Monad m) => (n a -> m a) -> m (n a) -> m a 
joinWith i = join . (fmap i) 

bindWith :: (Functor m, Monad m) => (n a -> m a) -> m a -> (a -> n a) -> m a 
bindWith i x f = joinWith i $ fmap f x 

*Main> let maybeToList = (\x -> case x of Nothing -> []; (Just y) -> [y]) 
*Main> bindWith maybeToList [1..9] (\x -> if even x then Just x else Nothing) 
[2,4,6,8] 
+2

Questa è una buona risposta. La mia comprensione è che si adatta bene anche alla teoria: la funzione 'n a -> m a' rappresenta una trasformazione naturale tra i funtori' n' e 'm'. Supponendo che rispetti le operazioni di monade, rappresenta anche un monomomodismo tra 'n' e' m'. –

+0

Scusate se la vostra risposta è intelligente, questo non risponde alla domanda che è "Più metodi flatMap per una singola monade?". Certamente possiamo trovare un modo per fondere due monadi differenti in un prescelto, ma facendo questo tipo di trasformazione porta anche a una monade? E non vedo come la tua risposta possa aiutarlo a capire perché non è una monade. – zurgl

+0

questa risposta è quasi perfetta, imo. L'ulteriore cosa che vorrei sottolineare è che se tu avessi una funzione 'ma -> (a -> nb) -> nb' che obbediva alle leggi monadiche, potresti costruire un morfismo monade' ma -> nb' passandole un ' return' come secondo argomento. Simillarmente, una funzione come 'm a -> (a -> n b) -> m b' può essere trasformata in un morfismo monad 'n a -> m b' con un lavoro più sleale. –

1

Dipende da cosa significa "dare un senso".

Se vuoi dire che è coerente con le leggi della monade, allora non è esattamente chiaro per me che la domanda abbia perfettamente senso. Dovrei vedere una proposta concreta da dire. Se lo fai nel modo in cui penso che suggerisci, probabilmente finirai per violare la composizione almeno in alcuni casi particolari.

Se vuoi dire che è utile, certo, puoi sempre trovare casi in cui queste cose sono utili. Il problema è che se inizi a violare le leggi monad, hai lasciato trappole nel tuo codice per il ragionatore funzionale (teoria delle categorie). Meglio rendere le monade come monade (e solo una alla volta, anche se puoi fornire un modo esplicito per passare a la Either - ma hai ragione che come scritto LeftProjection e RightProjection non lo sono, rigorosamente parlando, monadi). O scrivi documenti davvero chiari che spiegano che non è quello che sembra. Altrimenti qualcuno andrà allegramente ad assumere le leggi in attesa, e * splat *.

1

Non ha senso, per uno specifico tipo di dati, per quanto ne so, è possibile avere una sola definizione per bind.

in Haskell una monade è la seguente classe tipo,

instance Monad m where 
    return :: a -> m a 
    bind :: m a -> (a -> m b) -> m b 

Concretamente Per l'elenco Monade che abbiamo,

instance Monad [] where 
    return :: a -> [] a 
    (>>=) :: [] a -> (a -> [] b) -> [] b 

Consideriamo ora una funzione monadica come.

actOnList :: a -> [] b 
.... 

un caso d'uso per illustrare,

$ [1,2,3] >>= actOnList 

Sulla funzione actOnList vediamo che una lista è un tipo di vincolo polimorfo da un altro tipo (qui []). Quindi quando parliamo dell'operatore di bind per la lista monad parliamo dell'operatore di bind definito da [] a -> (a -> [] b) -> [] b.

Cosa si vuole ottenere è un operatore di bind definito come [] Maybe a -> (a -> [] b) -> [] b, questa non una versione specializzati del primo, ma un'altra funzione e che lo riguardano tipo di firma ho davvero dubbi che possa essere il bind gestore di ogni tipo di monade come non restituisci ciò che hai consumato.Passerai sicuramente da una monade all'altra usando una funzione, ma questa funzione non è sicuramente un'altra versione dell'elenco di operatori di bind.

Questo è il motivo per cui ho detto: non ha senso, per uno specifico tipo di dati, per quanto ne so, è possibile avere una sola definizione per bind.

1

flatMap o (>>=) non digita il testo per il tuo esempio Option[Try[ ]]. In pseudo-Haskell notazione

type OptionTry x = Option (Try x) 

instance Monad OptionTry where 
    (>>=) :: OptionTry a -> (a -> OptionTry b) -> OptionTry b 
    ... 

Dobbiamo bind/flatMap per restituire un valore avvolta nello stesso contesto come valore di input.

Possiamo anche vedere questo guardando l'equivalente return/join implementazione di una Monade. Per OptionTry, join ha il tipo specializzato

instance Monad OptionTry where 
    join :: OptionTry (OptionTry a) -> OptionTry a 
    ... 

Dovrebbe essere chiaro con un po 'di strabismo che la parte "piatta" di flatMap è join (o concat per le liste in cui il nome deriva da).

Ora, è possibile che un singolo tipo di dati abbia più diversi bind s. Matematicamente, una Monade è in realtà il tipo di dati (o, in realtà, l'insieme di valori costituito dalla monade) insieme a le operazioni particolari bind e return. Diverse operazioni portano a diverse Monade (matematiche).

Problemi correlati