Ricerca…


introduzione

Applicative è la classe dei tipi f :: * -> * che consente l'applicazione della funzione sollevata su una struttura in cui la funzione è anche incorporata in quella struttura.

Osservazioni

Definizione

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

Nota il vincolo di Functor su f . La funzione pure restituisce il suo argomento incorporato nella struttura Applicative . La funzione infisso <*> (pronunciato "apply") è molto simile a fmap tranne con la funzione incorporata nella struttura Applicative .

Un'istanza corretta di Applicative dovrebbe soddisfare le leggi applicative , sebbene queste non siano applicate dal compilatore:

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

Definizione alternativa

Poiché ogni Functional applicativo è un Functor , fmap può sempre essere utilizzato su di esso; quindi l'essenza di Applicative è l'accoppiamento dei contenuti trasportati, così come la capacità di 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

Questa classe è isomorfa a Applicative .

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

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

Al contrario,

funit = pure ()

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

Esempi comuni di Applicativo

Può essere

Maybe è un funtore applicativo che contiene un valore possibilmente assente.

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

pure il valore dato in Maybe applicando Just to it. La funzione (<*>) applica una funzione racchiusa in un valore Maybe in un valore in Maybe . Se sono presenti sia la funzione che il valore (costruito con Just ), la funzione viene applicata al valore e viene restituito il risultato del wrapping. Se uno dei due manca, il calcolo non può proseguire e Nothing viene restituito.

elenchi

Un modo per gli elenchi per adattarsi alla firma del tipo <*> :: [a -> b] -> [a] -> [b] è di prendere il prodotto cartesiano delle due liste, accoppiando ciascun elemento del primo elenco con ciascun elemento del secondo:

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

pure x = [x]

Questo è di solito interpretato come un emondeterminismo emulato, con una lista di valori che sta per un valore non deterministico i cui possibili valori spaziano su quella lista; quindi una combinazione di due valori non deterministici varia su tutte le possibili combinazioni dei valori nelle due liste:

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

Stream infiniti e elenchi di indirizzi

C'è una classe di s Applicative che "zip" i loro due input insieme. Un semplice esempio è quello di flussi infiniti:

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

Applicative istanza Applicative Stream applica un flusso di funzioni a un flusso di argomenti in senso critico, accoppiando i valori nei due flussi per posizione. pure restituisce un flusso costante - una lista infinita di un singolo valore fisso:

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

Anche le liste ammettono un'istanza Applicative "zippy", per la quale esiste il nuovo tipo ZipList :

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

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

Poiché zip taglia il suo risultato secondo l'input più breve, l'unica implementazione di pure che soddisfa le leggi Applicative è quella che restituisce una lista infinita:

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

Per esempio:

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

Le due possibilità ci ricordano il prodotto esterno e quello interno, simili a moltiplicare una matrice di 1 colonna ( nx 1 ) con una 1 riga ( 1 xm ) nel primo caso, ottenendo come risultato la matrice nxm (ma appiattita ); o moltiplicando le matrici di 1 riga e 1 colonna (ma senza il riepilogo) nel secondo caso.

funzioni

Quando sono specializzati in funzioni (->) r , le firme di tipo di pure e <*> corrispondono a quelle dei combinatori K e S , rispettivamente:

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

pure deve essere const , e <*> prende una coppia di funzioni e le applica a un argomento fisso, applicando i due risultati:

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

Le funzioni sono l'applicativo prototipo "zippy". Ad esempio, poiché gli stream infiniti sono isomorfi a (->) 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))

... rappresentando i flussi in un modo più alto produce automaticamente l'istanza Applicative zippata.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow