Questo probabilmente non è la risposta che state cercando, ma qui si va in ogni modo:
Un modo davvero storto di guardare monadi & co.
Un modo di considerare concetti astratti come questi è collegarli a concetti di base, come le operazioni di elaborazione di elenchi ordinari. Quindi, si potrebbe dire che,
- Una categoria generalizza l'operazione
(.)
.
- Un monoide generalizza l'operazione
(++)
.
- Un functor generalizza l'operazione
map
.
- Un funtore applicativo generalizza l'operazione
zip
(o zipWith
).
- Una monade generalizza l'operazione
concat
.
A Categoria
Una categoria è costituita da un insieme (o una classe) di oggetti e mazzo di frecce che ogni collegano due degli oggetti. Inoltre, per ogni oggetto, dovrebbe esserci una freccia di identità che collega questo oggetto a se stesso. Inoltre, se è presente una freccia (f
) che termina su un oggetto e un'altra (g
) che inizia dallo stesso oggetto, dovrebbe essere presente anche una freccia composta denominata g . f
.
In Haskell questo è modellato come un typeclass che rappresenta la categoria di tipi Haskell come oggetti.
class Category cat where
id :: cat a a
(.) :: cat b c -> cat a b -> cat a c
Gli esempi di base di una categoria sono funzioni. Ogni funzione collega due tipi, per tutti i tipi, c'è la funzione id :: a -> a
che collega il tipo (e il valore) a se stesso. La composizione delle funzioni è la composizione ordinaria delle funzioni.
Insomma, categorie in base di Haskell sono cose che si comportano come funzioni, vale a dire si può mettere uno dopo l'altro con una versione generalizzata di (.)
.
Un Monoide
Un monoid è un insieme con un elemento unitario e un'operazione associativa. Questo è modellato in Haskell come:
class Monoid a where
mempty :: a
mappend :: a -> a -> a
Esempi comuni di monoidi includono:
- insieme degli interi, l'elemento 0, e l'operazione
(+)
.
- set di numeri interi positivi, l'elemento 1 e l'operazione
(*)
.
- insieme di tutte le liste, la lista vuota
[]
e l'operazione (++)
.
Questi sono modellati in Haskell come
newtype Sum a = Sum {getSum :: a}
instance (Num a) => Monoid (Sum a) where
mempty = Sum 0
mappend (Sum a) (Sum b) = Sum (a + b)
instance Monoid [a] where
mempty = []
mappend = (++)
monoidi sono abituati a 'combinare' e accumulare cose. Ad esempio, la funzione mconcat :: Monoid a => [a] -> a
può essere utilizzata per ridurre un elenco di somme a somma singola o un elenco nidificato in un elenco semplice. Consideralo come una sorta di generalizzazione delle operazioni (++)
o (+)
che in un modo "uniscono" due cose.
un funtore
Un funtore in Haskell è una cosa che generalizza abbastanza direttamente l'operazione map :: (a->b) -> [a] -> [b]
. Invece di mappare su un elenco, esegue il mapping sulla struttura , ad esempio un elenco, un albero binario o anche un'operazione IO. Funtori sono modellate come questo:
class Functor f where
fmap :: (a->b) -> f a -> f b
Contrasto questo per la definizione della funzione normale map
.
Un applicativi Functor
funtori applicativi possono essere visti come le cose con una zipWith
un'operazione generalizzata. I Functor mappano su strutture generali una alla volta, ma con un functor Applicativo puoi comprimere due o più strutture. Per l'esempio più semplice, è possibile utilizzare applicativi per zip insieme due interi all'interno del tipo Maybe
:
pure (+) <*> Just 1 <*> Just 2 -- gives Just 3
Si noti che la struttura può influenzare il risultato, ad esempio:
pure (+) <*> Nothing <*> Just 2 -- gives Nothing
Contrasto questo al solito zipWith
funzione:
zipWith (+) [1] [2]
Invece di di soli liste, i lavori applicativi per tutti i tipi di strutture. Inoltre, il trucco intelligente con pure
e (<*>)
generalizza lo zipping per funzionare con qualsiasi numero di argomenti. Per vedere come funziona, controllare i seguenti tipi mantenendo il concetto di funzioni parzialmente applicate a mano:
instance (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
noti anche la somiglianza tra fmap
e (<*>)
.
monade
Monadi sono spesso utilizzati per modellare diversi contesti computazionali, come la non-deterministico, o lato-effectful calcoli. Dal momento che ci sono già troppi tutorial su Monad, mi limiterò a raccomandare lo The best one, invece di scriverne un altro.
In relazione alle normali funzioni di elaborazione dell'elenco, le monadi generalizzano la funzione concat :: [[a]] -> [a]
per lavorare con molti altri tipi di strutture oltre alle liste.Come semplice esempio, l'operazione monadica join
può essere utilizzato per appiattire nidificate Maybe
valori:
join (Just (Just 42)) -- gives Just 42
join (Just (Nothing)) -- gives Nothing
Come è legato all'utilizzo di Monadi come mezzo di calcoli strutturazione? Considera un esempio di giocattolo in cui esegui due query consecutive da un database. La prima query ti restituisce un valore chiave, con il quale desideri eseguire un'altra ricerca. Il problema qui è che il primo valore è racchiuso all'interno di Maybe
, quindi non è possibile eseguire una query direttamente. Invece, come forse è un Functor
, potresti invece fmap
il valore di ritorno con la nuova query. Questo ti darebbe due valori nidificati Maybe
come sopra. Un'altra query risulterebbe in tre livelli di Maybe
s. Questo sarebbe piuttosto difficile da programmare con, ma un monadico join
ti offre un modo per appiattire questa struttura e lavorare con un solo livello di Maybe
s.
(credo che sarò editing questo post molto prima che abbia un senso ..)
Hai letto il [Typeclassopedia] (http://www.haskell.org/haskellwiki/Typeclassopedia)? – dave4420
Grazie per il vostro consiglio. Avrei dovuto leggerlo prima. – Znatz
[Le Monade sono come i burritos] (http://blog.plover.com/prog/burritos.html). Veramente. –