En programació funcional una mònada és un TAD sense tipus concrets, corresponent a una estructura algebraica d'un sol element (d'aquí el nom de mònada), on la finalitat de les operacions és modelar la composició i la seqüencialitat de les computacions (accions amb efectes) mitjançant l'encadenament, separant la composició temporal, de l'execució, així com incorporar el resultat de cada operació sobre l'entorn.Haskellwiki - La mònada
La mònada modela una computació i pot ser vista com una acció amb efecte (un canvi), que produeix un resultat.
Les mònades es poden compondre, mitjançant l'encadenament, amb operacions sobre el resultat de la precedent (que poden incloure efectes col·laterals), estalviant, respecte de la programació imperativa, les variables locals emprades per aquesta per desar resultats.
(return v) representa una computació elemental que ofereix v com a resultat. return és el generador de la mònada. (>>=), anomenada bind, és la composició de computacions, encadenant una acció amb la següent en forma de funció sobre el resultat de la precedent.
En notació Haskell: -- generador d'una computació simple, que retorna una expressió de tipus `a` com a resultat return :: a -> efecte a
-- composició de computacions per encadenament amb el resultat (>>=) :: efecte a -> (a -> efecte b) -> efecte b -- compon amb una altra, com a funció del resultat de la precedent . -- tipus de l'acció acció :: efecte tipusDelResultat
-- exemple d'ús amb els operadors canònics: acció = return 5 -- acció simple que ofereix el valor com a resultat
acció = acció1 >>= funcAcció2 -- retorna el resultat de la darrera acció-funció -- que s'aplica al resultat de la primera
-- exemple amb niuament progressiu per retornar una expressió de resultats precedents acció = acció1 >>= \resultat1 -> (acció2 >>= \resultat2 -> return expressió_de_resultats_precedents)
-- el mateix amb notació monàdica (sintaxi especial) denotant la precedència temporal acció = do resultat1 <- acció1 -- sintaxi: resultat "<-" acció_d'efectes resultat2 <- acció2 ... let resultatNou = expressió_de_resultats_precedents return resultatNou -- acció simple que retorna el valor
La mònada (signatura) s'implementa per a un tipus (estructura) que pot ser vist com un contenidor d'un sol element que pot ser el resultat, però no sempre és el resultat tot sol. #La mònada Writer aparella com a valor el resultat i un element combinable (un Monoide). #La mònada Reader conté com a valor una funció de tipus (entorn -> resultat). #La mònada State conté com a valor una altra tipus de funció (estat -> (resultat, nouEstat)).
Té aplicació en llenguatges de programació no-estrictes, on l'ordre de les operacions és indeterminat, principalment al Haskell i també en altres àmbits, en entorns on l'ordre de les operacions no està garantit, com ara encadenament de transaccions en memòria (mònada STM a Haskell i a OCaml), encadenament d'operacions asíncrones (mònada Par a Haskell, Asynchronous Workflows a F#), etc.
Això facilita als lleng. funcionals complementar la part funcional pura, amb efectes com ara operacions d'entrada/sortida, ops. sobre l'entorn exterior, canvis d'estat, i també el preprocés i optimització de les operacions abans de la seva execució.
Les lleis de la mònada.Haskell wiki - Monad laws
return x >>= f≡f x-- identitat per l'esquerra
m >>= return≡m-- identitat per la dreta
m >>= (\x -> f x >>= g)≡(m >>= f) >>= g<td>-- associativitat<td>
Al llenguatge Haskell la classe de tipus Monad descriu la signatura de les operacions descrites, a banda d'altres mètodes amb motius pràctics: class Monad efecte where -- generador return :: a -> efecte a -- generador d'una "computació" a partir -- d'un valor de tipus ''a'' interpretable com a resultat
{- encadenament (ang.: ''bind'') encadena un efecte amb una funció efecte sobre el seu resultat -} (>>=) :: efecte a -> (a -> efecte b) -> efecte b
-- exemple: getline >>= putStrLn -- imprimeix la línia introduïda (resultat de l'efecte precedent)
{- la classe Monad també incorpora els següents mètodes, per qüestions pràctiques però que no fan falta per la descripció teòrica: -}
{-| (>>) modela un encadenament que ignora el resultat de la computació precedent. -} (>>) :: efecte a -> efecte b -> efecte b m_x >> m_y = m_x >>= (\_ -> m_y) -- implementació per defecte
-- el mètode `fail` per disparar error cas de fallar l'encaix a la variable del resultat -- A partir de GHC 8.8 desapareix de Monad i obliga que els encaixos siguin irrefutables -- Per a l'ús de patrons refutables als encaixos caldrà que la mònada implementi MonadFail fail :: String -> IO a fail missatge
La funció `fail` s'utilitza en la sintaxi específica de blocs d'encadenaments monàdics, els blocs do, per disparar una excepció en cas que falli l'encaix del patró del paràmetre de la funció d'efectes, però serà expulsada de la classe Mònada a GHC 8.8, cap a una classe específica MonadFail de Control.Monad.Fail.La classe MonadFail L'ús de patrons en l'encadenament requerirà que la mònada implementi MonadFail evitant les petades per l'ús de patrons en mònades que no els suporten.La proposta MonadFail Fase final de la proposta implementada a GHC 8.8.GHC 8.8.1 Notes de la versió La fase final de la proposta MonadFail (exclusió de `fail`) hi ve implementada.
En Haskell podem expressar (>>=), en termes de fmap i join, doncs la classe Monad actualment deriva de la classe Applicative que deriva al seu torn de Functor. El nom "flatMap" que (>>=) pren en altres llenguatges (Scala, Java8) ve de l'ús d'aquests conceptes:
-- ''join'' aplana el tipus. join :: Monad efecte => efecte (efecte a) -> efecte a
-- Si l'efecte implementa l'estructura Functor fmap :: Functor efecte => (a -> b) -> efecte a -> efecte b
-- (>>=) :: efecte a -> (a -> efecte b) -> efecte b m_a >>= f = (join. fmap f) m_a -- d'aquí el nom de flatMap en altres llenguatges
Al llenguatge OCaml: Mònada per a les computacions IO no blocants.
(* De la biblioteca Light Weight Threads. A l'OCaml els tipus s'apliquen en sentit invers (de dreta a esq.) *)
module LWT = sig type +'a t (* The type of threads returning a result of type 'a. *) val return : 'a -> 'a t val (>>=) : 'a t -> ('a -> 'b t) -> 'b t val fail : exn -> 'a t ... end ;;
A Scala la mònada és el substrat de les llistes per comprensió:FilterMonadic Vegeu #Mònades a Scala més avall.
trait FilterMonadic[+A, +Repr] extends Any { // flatMap: encadenament (equivalent del Haskell (>>=)) abstract def flatMap[B, That](f: (A) ⇒ GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That
// la transformació final en una llista per comprensió requereix que implementi Functor // l'aplicació del mètode (map f) converteix la coŀleció Repr en una del tipus de sortida That abstract def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That
abstract def withFilter(p: (A) ⇒ Boolean): FilterMonadic[A, Repr] abstract def foreach[U](f: (A) ⇒ U): Unit }
A Java8 la classe d'objectes Optional implementa operacions similars a les de Scala:La classe d'objectes Optional<T> Class Optional<T> public static <T> Optional<T> of(T value) // equivalent del Haskell (return) public <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) // equivalent del Haskell (>>=) public <U> Optional<U> map(Function<? super T, ? extends U> mapper) // equivalent del Haskell (fmap) Optional<T> filter(Predicate<? super T> predicate) ...
Mònades segons la quantificació de resultats Seqüenciació d'accions amb resultat únic: IO: entrada/sortida, excepcions I/O i canvis d'estat de variables globals IORef. ST: encapsulament de canvis d'estat en un àmbit local (variables STRef). resultat opcional (de funcions o bé d'accions que poden fallar): la mònada Maybe: èxit o fallada de funcions definides parcialment. la mònada (Either tipusError): èxit o fallada de la validació amb try d'accions que poden llançar excepcions. resultat múltiple: la mònada llista (mecanisme de les llistes per comprensió).
Mònades i estrictesa Les mònades no impliquen estrictesa (avaluació del primer argument de (>>=) malgrat que el segon no en demani el resultat com a paràmetre: cas del comodí '_') sinó que depèn de la implementació.Monads are not about strictness La mònada IO és d'avaluació estricta, la mònada llista [a] no, i per exemple la mònada State té versions estrictaControl.Monad.ST.Strict i d'avaluació tardana (lazy)Control.Monad.ST.Lazy
Mònades amb efecte fallada Afegint la llei de l'element absorbent per l'esquerra
mzero >>= f ≡ mzero
L'element absorbent per l'esquerra fa inútil l'avaluació de les computacions subseqüents, ja que el resultat és el mateix valor, i pot indicar una situació d'error que invalidi la continuació de les computacions.
L'encadenament de computacions amb mònades que compleixin la regla de l'element absorbent en (>>=) per algun valor del tipus, evita haver de comprovar resultats d'error a cada pas nou en la seqüència.
La mònada Maybe (computacions que poden fallar) Nothing n'és l'element absorbent per l'esquerra. Optimitza la seqüència evitant l'execució de computacions subseqüents a la que falla. -- de la definició
data Maybe a = Nothing | Just a
instance Monad Maybe where return x = Just x fail _ = Nothing
Nothing >>= _ = Nothing -- element absorbent; no avalua la computació subseqüent
(Just x) >>= f = f x -- f :: a -> Maybe b
Exemple: headMaybe :: [a] → Maybe a headMaybe (x : _) = return x -- resultat exitós, equival a (Just x) headMaybe [] = Nothing -- element absorbent de la mònada Maybe
doblar :: Num a => a → Maybe a doblar x = return $ 2 * x
doblarElCap :: Num a => [a] → Maybe a doblarElCap llista = headMaybe llista >>= doblar
-- equivalent amb notació de blocs ''do'' doblarElCap llista = do x <- headMaybe llista doblar x -- cas de llista buida, ''doblar'' no s'avalua
-- doblarElCap [] == Nothing -- doblarElCap [1,2,3] == Just 2
La mònada Either (resultats duals) Amplia el valor Nothing de la mònada Maybe amb un segon conjunt de valors, emprat sovint per designar els errors en computacions que poden fallar disparant excepcions.
Els constructors Left i Right, a més de l'accepció d'Esquerre i Dreta, també tenen el significat de Tort i Dret, explicitant així la correcció del resultat.
Tots els elements amb constructor Left (Left l) són elements absorbents per l'esquerra.
data Either a b = Left a | Right b
instance Monad (Either e) where return x = Right x
Left l >>= _ = Left l -- element absorbent, l'encadenament no s'avalua
Right r >>= f = f r Exemple: -- `try` de Control.Exception avalúa una acció que pot disparar excepcions retornant l'excepció o el resultat try :: Exception e => IO a -> IO (Either e a)
-- `evaluate` de Control.Exception avalúa una expressió funcional per fer-ne aflorar les excepcions evaluate :: a -> IO a -- força l'avaluació (a WHNF)
-- exemple: import Data.Fixed (Centi) -- coma fixa centessimal per a valors monetaris provaDivisió :: Centi -> Centi -> Either ArithException Centi provaDivisió x y = try (evaluate (x/y))
Composició monàdica Composició de Kleisli.Composició de Kleisli (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
La mònada IO Haskell modela l'entrada/sortida com una seqüència encadenada d'operacions (per forçar-ne la serialització degut a l'avaluació no-estricta), encapsulant els canvis d'estat d'accés a fitxers. Aquest model encaixa amb el concepte de mònada, donant lloc a la mònada IO.
La funció inicial main de qualsevol programa en Haskell ha de ser una expressió d'operacions d'ent./sortida i, per tant, del tipus de la mònada IO.
-- expressions de la ''mònada'' IO (el tipus porta com a paràmetre el tipus del resultat de les operacions) getLine -- captura una línia introduïda -- tipus ''IO String'' getLine >>= putStr -- imprimeix la línia introduïda -- tipus ''IO ()''