Recherche…


Introduction

Applicative est la classe de types f :: * -> * qui permet une application de fonction soulevée sur une structure où la fonction est également incorporée dans cette structure.

Remarques

Définition

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

Notez la contrainte Functor sur f . La fonction pure renvoie son argument incorporé dans la structure Applicative . La fonction infixe <*> (prononcée "apply") est très similaire à fmap sauf avec la fonction intégrée dans la structure Applicative .

Une instance correcte de Applicative devrait satisfaire aux lois applicatives , bien que celles-ci ne soient pas appliquées par le compilateur:

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

Définition alternative

Comme chaque foncteur applicatif est un foncteur , fmap peut toujours y être utilisé; Ainsi, l'essence de Applicative est l'appariement des contenus transportés, ainsi que la possibilité de le créer:

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

Cette classe est isomorphe à Applicative .

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

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

Inversement,

funit = pure ()

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

Instances courantes d'application

Peut être

Maybe - Maybe est-ce un foncteur applicatif contenant une valeur éventuellement absente.

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

pure lève la valeur donnée dans Maybe en lui appliquant Just . La fonction (<*>) applique une fonction enveloppée dans un Maybe à une valeur dans Maybe . Si la fonction et la valeur sont présentes (construites avec Just ), la fonction est appliquée à la valeur et le résultat encapsulé est renvoyé. Si l'un ou l'autre est manquant, le calcul ne peut pas continuer et Nothing n'est renvoyé à la place.

Des listes

Une façon pour les listes de correspondre à la signature de type <*> :: [a -> b] -> [a] -> [b] consiste à prendre le produit cartésien des deux listes, en associant chaque élément de la première liste à chaque élément du second:

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

pure x = [x]

Ceci est généralement interprété comme une émulation du non-déterminisme, avec une liste de valeurs représentant une valeur non-déterministe dont les valeurs possibles sont comprises dans cette liste; ainsi, une combinaison de deux valeurs non déterministes se situe sur toutes les combinaisons possibles des valeurs des deux listes:

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

Flux infinis et listes zip

Il y a une classe de Applicative qui "compressent" leurs deux entrées ensemble. Un exemple simple est celui des flux infinis:

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

Applicative instance Applicative Stream applique un flux de fonctions à un flux d'arguments au niveau des points, en associant les valeurs des deux flux par position. pure renvoie un flux constant - une liste infinie d'une seule valeur fixe:

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

Les listes admettent également une instance Applicative "zippy", pour laquelle il existe le newtype ZipList :

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

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

Puisque zip ajuste son résultat en fonction de l'entrée la plus courte, la seule implémentation de pure satisfaisant aux lois Applicative est celle qui renvoie une liste infinie:

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

Par exemple:

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

Les deux possibilités nous rappellent le produit externe et interne, similaire à la multiplication d’une matrice à 1 colonne ( nx 1 ) avec une matrice à 1 rangée ( 1 xm ) dans le premier cas, obtenant la matrice nxm (mais aplatie ); ou en multipliant les matrices à une ligne et à une colonne (mais sans les additionner) dans le second cas.

Les fonctions

Lorsqu'elles sont spécialisées dans les fonctions (->) r , les signatures de type pure et <*> correspondent respectivement à celles des combinateurs K et S :

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

pure doit être const , et <*> prend une paire de fonctions et les applique à un argument fixe, en appliquant les deux résultats suivants:

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

Les fonctions sont le prototypique "zippy" applicative. Par exemple, puisque les flux infinis sont isomorphes à (->) 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 représentation des flux de manière plus ordonnée produit automatiquement l'instance Applicative zippy.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow