Buscar..


Introducción

Applicative es la clase de tipos f :: * -> * que permite la aplicación de funciones elevadas sobre una estructura donde la función también está incrustada en esa estructura.

Observaciones

Definición

class Functor f => Applicative f where
    pure  :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

Tenga en cuenta la restricción de Functor en f . La función pure devuelve su argumento incrustado en la estructura Applicative . La función de infijo <*> (pronunciada "aplicar") es muy similar a fmap excepto con la función incorporada en la estructura Applicative .

Una instancia correcta de Applicative debería satisfacer las leyes aplicativas , aunque estas no son aplicadas por el compilador:

pure id <*> a = a                              -- identity
pure (.) <*> a <*> b <*> c = a <*> (b <*> c)   -- composition
pure f <*> pure a = pure (f a)                 -- homomorphism
a <*> pure b = pure ($ b) <*> a                -- interchange

Definición alternativa

Dado que cada fmap Functor es un Functor , fmap siempre se puede usar en él; Por lo tanto, la esencia de Applicative es el emparejamiento de contenidos transportados, así como la capacidad de crearlo:

class Functor f => PairingFunctor f where
  funit :: f ()                  -- create a context, carrying nothing of import
  fpair :: (f a,f b) -> f (a,b)  -- collapse a pair of contexts into a pair-carrying context

Esta clase es isomorfa al Applicative .

pure a = const a <$> funit = a <$ funit  

fa <*> fb = (\(a,b) -> a b) <$> fpair (fa, fb) = uncurry ($) <$> fpair (fa, fb)

A la inversa,

funit = pure ()

fpair (fa, fb) = (,) <$> fa <*> fb

Ejemplos comunes de aplicativo

Tal vez

Maybe sea ​​un funtor aplicativo que contenga un valor posiblemente ausente.

instance Applicative Maybe where
    pure = Just
    
    Just f <*> Just x = Just $ f x
    _ <*> _ = Nothing

pure levanta el valor dado en Maybe aplicando Just a él. La función (<*>) aplica una función envuelta en un Maybe a un valor en Maybe . Si tanto la función como el valor están presentes (construidos con Just ), la función se aplica al valor y se devuelve el resultado final. Si falta alguno, el cálculo no puede continuar y, en su lugar, Nothing se devuelve Nothing .

Liza

Una forma para que las listas se ajusten a la firma de tipo <*> :: [a -> b] -> [a] -> [b] es tomar el producto cartesiano de las dos listas, emparejando cada elemento de la primera lista con cada uno Elemento del segundo:

fs <*> xs = [f x | f <- fs, x <- xs]
         -- = do { f <- fs; x <- xs; return (f x) }

pure x = [x]

Esto generalmente se interpreta como emulando el no determinismo, con una lista de valores representando un valor no determinista cuyos valores posibles se extienden sobre esa lista; por lo tanto, una combinación de dos valores no deterministas abarca todas las combinaciones posibles de los valores en las dos listas:

ghci> [(+1),(+2)] <*> [3,30,300]
[4,31,301,5,32,302]

Flujos infinitos y listas zip

Hay una clase de Applicative que "comprimen" sus dos entradas juntas. Un ejemplo simple es el de las corrientes infinitas:

data Stream a = Stream { headS :: a, tailS :: Stream a }

La instancia Applicative Stream aplica un flujo de funciones a un flujo de argumentos puntuales, emparejando los valores en los dos flujos por posición. pure devuelve una secuencia constante, una lista infinita de un solo valor fijo:

instance Applicative Stream where
    pure x = let s = Stream x s in s
    Stream f fs <*> Stream x xs = Stream (f x) (fs <*> xs)

Las listas también admiten una instancia Applicative "zippy", para la que existe el ZipList ZipList:

newtype ZipList a = ZipList { getZipList :: [a] }

instance Applicative ZipList where
    ZipList xs <*> ZipList ys = ZipList $ zipWith ($) xs ys

Dado que zip recorta su resultado de acuerdo con la información más corta, la única implementación de pure que cumple con las leyes de Applicative es una que devuelve una lista infinita:

    pure a = ZipList (repeat a)   -- ZipList (fix (a:)) = ZipList [a,a,a,a,...

Por ejemplo:

ghci> getZipList $ ZipList [(+1),(+2)] <*> ZipList [3,30,300]
[4,32]

Las dos posibilidades nos recuerdan el producto externo y el interno, similar a multiplicar una matriz de 1 columna ( nx 1 ) con una de 1 fila ( 1 xm ) en el primer caso, obteniendo la matriz nxm como resultado (pero aplanada) ); o multiplicar las matrices de 1 fila y 1 columna (pero sin resumir) en el segundo caso.

Funciones

Cuando se especializa en funciones (->) r , las firmas de tipo pure y <*> coinciden con las de los combinadores K y S , respectivamente:

pure :: a -> (r -> a)
<*> :: (r -> (a -> b)) -> (r -> a) -> (r -> b)

pure debe ser const , y <*> toma un par de funciones y las aplica a un argumento fijo, aplicando los dos resultados:

instance Applicative ((->) r) where
    pure = const
    f <*> g = \x -> f x (g x)

Las funciones son el prototípico "zippy" aplicativo. Por ejemplo, dado que las corrientes infinitas son isomorfas para (->) Nat , ...

-- | Index into a stream
to :: Stream a -> (Nat -> a)
to (Stream x xs) Zero = x
to (Stream x xs) (Suc n) = to xs n

-- | List all the return values of the function in order
from :: (Nat -> a) -> Stream a
from f = from' Zero
    where from' n = Stream (f n) (from' (Suc n))

... la representación de flujos en una orden superior produce automáticamente la instancia de Zippy Applicative .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow