Monads en zo

Monads en zo

Wat ik er nu van begrijp

(Opmerkingen / aanvullingen dus welkom.)

Laws

Het gaat steeds om een set S met bijbehorende binary operatie O.
Sommige "synoniemen" betreffen weliswaar dezelfde operatie, maar horen bij verschillende niveaus. Zo is Ap de Monad-versie van <*> (Applicative Functor-versie).

Het nut van Monads voor de programmeur

Ik spreek hier alleen over Monads. Het zal duidelijk zijn dat voor sommige toepassingen het zijn van bijvoorbeeld Functor toereikend is.


I. Semigroep
Voorbeelden
Constructor: *
Set gehele getallen met + of * operatie: (4 + 10) + 2 = 4 + (10 +2)
Voorbeelden zonder identity: positieve gehele getallen met + operator, of niet-lege sequenties.
Laws
Closure en Associativity
Operatie
append

II. Monoid
Voorbeelden
Set gehele getallen met + of * operatie: a+0=a, a*1=a
Lists, Bool, Ordering, Maybe, zelfgedefinieerde Tree.
Verzameling met operatie, die je (recursief) kunt uitbreiden.
Laws Als Semigroep, met Identity
  • mempty `mappend` x = x
  • x `mappend` mempty = x
  • (x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)
mempty vertegenwoordigt het identity-element voor een bepaalde monoid met operatie, mappend is de binaire functie.
mconcat is een secundaire functie die een lijst monoids via een fold-operatie reduceert tot een enkele waarde.
(mconcat is een ongelukkige naam, bij een optelling bijvoorbeeld wordt er niets aan elkaar geplakt.)
Operaties
Als Semigroep, aangevuld met de zero-operatie.



III. Functor
Voorbeelden
Unary type constructor:  *->*
Binary type constructor: *->*->*
Functor[F[_]]

Scala: List, Option, Set, Either, Try, Future...
Haskell: List, Maybe, eigen Tree, Either, Function ...
"Alles waarover gemapt kan worden", het toepassen van een functie op waarde[n] in een context.
Mapping over een functie is functie-composition (Nicholas, zie ook LYAH Functor)
Laws Identity en Compose
1. Indentity
fmap id = id
2. Compose
fmap (f . g) = fmap f . fmap g , oftewel
fmap (f . g) F = fmap f (fmap g F)
Operatie Eén typeclass-methode: fmap :: (a -> b) -> f a -> f b
(map is de List-implementatie van fmap)
Map, Either, Function1 zijn binary, daarbij moet de operatie partieel, in stappen worden toegepast.

IV. Applicative (-Functor)
Voorbeelden Voorlopig: De toegepaste functie kan zich zelf ook binnen een context bevinden
Laws
  • pure f <*> x == fmap f x
  • pure id <*> v == v
  • pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
  • pure f <*> pure x = pure (f x)
  • u <*> pure y = pure ($ y) <*> u
Operaties Als Functor, met Pure (Wrapt value in applicative),
en ap of <*>(Neemt een applicative met een functie er in en een andere applicative, extraheert de functie(s) uit de eerste en mapt die over de tweede).
Applicatives zijn composable, wat niet voor elke Monad zo is.

V. Monad
Voorbeelden
Higher order type operator: (*->*)->*
Monad[M[_]]

M[_] staat voor een unary type-constructor als List of Option
Scala: List, Option, Set, Either, Try, Future...
Haskell: List, Maybe, Tree?, Either, ...
"Informeel is een Monad alles met een constructor en een flatMap methode."
Laws
Left identity
return x >>= f is het zelfde als f x
Right identity
m >>= return is gelijk aan m
Associativity
Het resultaat van (m >>= f) >>= g is gelijk aan dat van m >>= (\x -> f x >>= g)
Operaties

class Applicative m => Monad m where
    return :: a -> m a
    (>>=)  :: m a -> (a -> m b) -> m b
 
    (>>)   :: m a -> m b -> m b
    fail   :: String -> m a
Als Applicative, met bind, >>= of do-notation. (In Scala: flatMap.)

Naast bind dient return voor het wrappen van een waarde in de monad (een "lift"-functie, zoals pure bij de applicative), en >> ("then") voor het chainen van monadische acties die het resultaat van de voorganger niet nodig hebben. fail is alleen intern nodig voor pattern-match failures in do-notation.

Net als de Applicative (-Functor) is ook Arrow een superclass van Monad, maar deze past niet helemaal in dit rijtje. Arrows missen een andere law en hebben een ander toepassingsgebied (o.a. parsers).

Copyright © 2020 Hans de Jong, all rights reserved