En programació funcional, un functor aplicatiu, en anglès applicative functor és una estructura algebraica que facilita la combinació de resultats d'una seqüència d'accions d'efectes laterals, sense la serialització temporal que requereixen les mònades, admetent paral·lelisme en la seva execució.
Els Functors Aplicatius van ser introduïts el 2007 per Conor McBride i Ross Paterson en el seu estudi "Funcional Pearl: programació aplicativa amb efectes".[1]
Els functors aplicatius van aparèixer primer com a característica de biblioteca en Haskell, però de llavors ençà s'ha estès a altres llengüatges, incloent Idris, Agda, i Scala. Ambdós Glasgow Haskell i Idris ofereixen característiques del llenguatge per facilitar la programació amb functors aplicatius.
En Haskell, és definit a la classe de tipusApplicative
definida al mòdul Control.Applicative.
Per raons històriques, en el llenguatge Haskell, els functors aplicatius no van ser implementats com a superclasse de Mònada
, sinó com a classe de tipus separada. Posteriorment la proposta Functor-Applicative-Monad de relació de les respectives classes de tipus va resultar en la incorporació de Applicative com a requeriment per a Monad a la versió 7.10 de GHC.[2]
Els Functors aplicatius componen les accions aplicant un resultat combinador de la primera al resultat de l'acció següent. Habitualment s'aplica un combinador amb l' fmap del Functor sobre la primera acció quin resultat serà l'aplicació parcial o no del combinador sobre el resultat de l'acció i que compondrem amb tantes accions com paràmetres manquin a l'aplicació parcial esmentada.
Expressa la combinació de computacions (amb efectes col·laterals) sense l'encadenament del resultat i per tant la serialització temporal que defineix la mònada. Signatura.[3]
class Functor efecte => Applicative efecte where
-- `pure` genera una dada del tipus de l'efecte partint d'un valor interpretable com a resultat
-- s'utilitza també per elevar un combinador a la categoria d'efecte per combinar els resultats de les accions posteriors amb (<*>)
pure :: a -> efecte a
-- aplica el combinador resultant del primer efecte al resultat del segon
(<*>) :: efecte (a -> b) -> efecte a -> efecte b
-- seqüència (verb seqüenciar) l'engegada de dos efectes, oferint com a resultat el del segon
(*>) :: efecte a -> efecte b -> efecte b
-- seqüència l'engegada de dos efectes, oferint com a resultat el del primer
(<*) :: efecte a -> efecte b -> efecte a
Requereix la classe Functor:[4]
class Functor contenidor where
fmap :: (a -> b) -> contenidor a -> contenidor b
-- les instàncies de ''functor'' han de complir
-- fmap id == id
-- fmap (f. g) == fmap f. fmap g
Per aplicar una funció de N arguments als resultats de N computacions en seqüència definides Applicative tenim les funcions liftAn i les expressions equivalents:
import Control.Applicative
-- definits liftA, liftA2, i liftA3 a Control.Applicative
(<$>) = fmap -- <$> definit a Data.Functor com a operador infix
{- apliquem una funció a la primera acció combinant-ne el resultat
obtenint un resultat combinador quina acció compondrem mitjançant (<*>) amb la resta
equival també a elevar la funció combinadora al tipus de l'acció amb `pure`
i compondre-la amb la resta.
-}
-- per a f d'un sol argument
liftA :: (Applicative m) => (a -> b) -> m a -> m b
liftA f x1 = f <$> x1 = pure f <*> x1
-- per a f2 de dos arguments
liftA2 :: (Applicative m) => (a1 -> a2 -> b) -> m a1 -> m a2 -> m b
liftA2 f2 x1 x2 = f2 <$> x1 <*> x2 = pure f2 <*> x1 <*> x2
-- per a fn :: a1 -> ... -> an -> b
liftAn fn x1 ... xn = fn <$> x1 <*> x2 <*> ... <*> xn
En relació amb la mònada tenim les següents equivalències
Applicative[3] | Monad[5] |
---|---|
pure |
return
|
(<*>) |
ap
|
(*>) |
(>>)
|
f_a <* f_b
|
f_a >>= (\x -> f_b >> return x)
-- el cos de la lambda abasta el màxim cap a la dreta !!
|
A la biblioteca async,[6] facilita l'execució simultània d'operacions I/O no blocants en fils d'execució individuals, on el tipus Concurrently[7] implementa un functor aplicatiu que permet combinar els resultats d'accions engegades asíncronament, esperant-ne la finalització conjunta, i també implementa Alternative per obtenir el resultat de la primera que acaba, anul·lant els fils d'execució de la resta d'operacions engegades.
L'extensió de Haskell ApplicativeDo demana al compilador generar codi aplicatiu partint de la sintaxi dels blocs 'do' de les mònades quan sigui possible:[8]
{-# LANGUAGE ApplicativeDo #-}
acció :: (Applicative m) => m r
acció = do
a <- ma
b <- mb
c <- mc
return $ f3 a b c
{- en aquest cas on les accions proposades no fan ús dels resultats precedents,
el compilador pot generar la composició típica del codi aplicatiu
f3 <$> ma <*> mb <*> mc
-}