Arrows
sono generalizzati per Categorie, quindi dalla classe Category
.
class Category f where
(.) :: f a b -> f b c -> f a c
id :: f a a
La definizione typeclass Arrow
ha Category
come superclasse. Le categorie (nel senso haskell) generalizzano le funzioni (è possibile comporle ma non applicarle) e quindi sono decisamente un "modello di calcolo". Arrow
fornisce uno Category
con struttura aggiuntiva per lavorare con le tuple. Quindi, mentre Category
rispecchia qualcosa sullo spazio delle funzioni di Haskell, Arrow
lo estende a qualcosa sui tipi di prodotto.
Ogni Monad
dà origine a qualcosa chiamato "Categoria Kleisli" e questa struttura fornisce istanze di ArrowApply
. Puoi creare un Monad
su qualsiasi ArrowApply
in modo tale che il giro completo non cambi il tuo comportamento, quindi in alcuni aspetti profondi Monad
e ArrowApply
sono la stessa cosa.
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
(Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)
instance Monad m => Arrow (Kleisli m) where
arr f = Kleisli (return . f)
first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))
realtà ogni Arrow
può giustificare un Applicative
(universalmente quantificata per ottenere i giusti tipi) oltre ai Category
superclasse, e credo che la combinazione del competente Category
e Applicative
è sufficiente per ricostruire il Arrow
.
Quindi, queste strutture sono profondamente connesse.
Avvertenza: posticipato in anticipo. Una differenza centrale tra la Functor
/Applicative
/Monad
modo di pensare e il modo Category
/Arrow
di pensare è che, mentre Functor
ed il suo ilk sono generalizzazioni al livello di oggetto (tipi a Haskell), Category
/Arrow
sono generelazation del nozione di morfismo (funzioni in Haskell). Credo che pensare al livello del morfismo generalizzato implichi un livello più alto di astrazione rispetto al pensare a livello di oggetti generalizzati . A volte è una buona cosa, altre volte no. D'altra parte, nonostante il fatto che lo Arrows
abbia una base categorica, e nessuno in matematica pensa che Applicative
sia interessante, sono convinto che lo Applicative
sia generalmente compreso meglio di Arrow
.
Fondamentalmente si può pensare di "Categoria < Freccia < ArrowApply" e "Functor < applicativo < Monade" tale che "Categoria ~ Functor", "Freccia ~ applicativo" e "ArrowApply ~ Monad".
Ulteriori cemento sottostante: Come per altre strutture per modello di calcolo: spesso si può invertire la direzione delle "frecce" (solo significato morphisms qui) nelle costruzioni categoriali per ottenere il "doppio" o "co-costruzione ". Così, se una monade è definito come
class Functor m => Monad m where
return :: a -> m a
join :: m (m a) -> m a
(ok, lo so che non è come Haskell definisce le cose, ma ma >>= f = join $ fmap f ma
e join x = x >>= id
in modo altrettanto bene potrebbe essere) poi il comonad è
class Functor m => Comonad m where
extract :: m a -> a -- this is co-return
duplicate :: m a -> m (m a) -- this is co-join
Questa cosa risulta essere abbastanza comune anche. Risulta che Comonad
è la struttura di base di base degli automi cellulari . Per completezza, vorrei sottolineare che di Edward Kmett Control.Comonad
mette duplicate
in una classe tra il funtore e Comonad
per "allungabili Funtori", perché si possono anche definire
extend :: (m a -> b) -> m a -> m b -- Looks familiar? this is just the dual of >>=
extend f = fmap f . duplicate
--this is enough
duplicate = extend id
Si scopre che tutti i Monad
s sono anche "allungabile"
monadDuplicate :: Monad m => m a -> m (m a)
monadDuplicate = return
mentre tutti Comonads
sono "assemblabili"
comonadJoin :: Comonad m => m (m a) -> m a
comonadJoin = extract
così
queste strutture sono molto vicine tra loro.
La pagina Monad vs. Frecce collega a un documento che confronta anche i funtori applicativi (detti anche idiomi). –
I funtori applicativi sicuramente * sono * buoni per il calcolo componibile! Infatti, essi compongono meglio delle monadi (la composizione di due funtori applicativi è un funtore applicativo, che non vale per le monadi). – ehird