Suche…


Einführung

Applicative ist die Klasse der Typen f :: * -> * die eine übergeordnete Funktionsanwendung über eine Struktur ermöglicht, in der die Funktion ebenfalls in diese Struktur eingebettet ist.

Bemerkungen

Definition

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

Beachten Sie die Functor Einschränkung für f . Die pure Funktion gibt ihr in die Applicative Struktur eingebettetes Argument zurück. Die Infix-Funktion <*> (ausgesprochen "zutreffend") ist fmap sehr ähnlich, abgesehen von der in die Applicative Struktur eingebetteten Funktion.

Eine korrekte Instanz von Applicative sollte die anwendbaren Gesetze erfüllen, diese werden jedoch vom Compiler nicht durchgesetzt:

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

Alternative Definition

Da jeder Applicative Functor ein Functor ist , kann fmap immer verwendet werden. Daher ist das Wesentliche von Applicative die Kombination der mitgeführten Inhalte sowie die Fähigkeit, diese zu erstellen:

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

Diese Klasse ist isomorph zu Applicative .

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

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

Umgekehrt,

funit = pure ()

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

Häufige Fälle von Applicativ

Könnte sein

Maybe ein applikativer Funktor einen möglicherweise fehlenden Wert.

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

pure hebt den angegebenen Wert in Maybe an, indem Sie Just auf ihn anwenden. Die Funktion (<*>) wendet eine Funktion, die in einem Maybe auf einen Wert in einem Maybe . Wenn sowohl die Funktion als auch der Wert vorhanden sind (mit Just ), wird die Funktion auf den Wert angewendet und das umschlossene Ergebnis wird zurückgegeben. Wenn einer fehlt, kann die Berechnung nicht fortgesetzt werden und stattdessen wird Nothing zurückgegeben.

Listen

Eine Möglichkeit, Listen an die Typunterschrift <*> :: [a -> b] -> [a] -> [b] besteht darin, das kartesische Produkt der beiden Listen zu verwenden, wobei jedes Element der ersten Liste mit jedem Paar verbunden wird Element des zweiten:

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

pure x = [x]

Dies wird normalerweise als Emulation des Nichtdeterminismus interpretiert, wobei eine Liste von Werten für einen nichtdeterministischen Wert steht, dessen mögliche Werte über dieser Liste liegen; Eine Kombination zweier nichtdeterministischer Werte erstreckt sich also auf alle möglichen Kombinationen der Werte in den beiden Listen:

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

Unendliche Streams und Zip-Listen

Es gibt eine Klasse von Applicative s, die ihre beiden Eingaben zusammenfassen. Ein einfaches Beispiel sind unendliche Ströme:

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

Die Applicative Instanz von Stream wendet einen Stream von Funktionen punktweise auf einen Stream von Argumenten an, wobei die Werte in den beiden Streams nach Position zusammengefasst werden. pure gibt einen konstanten Stream zurück - eine unendliche Liste eines einzelnen festen Werts:

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

Listen geben auch eine "flinke" Applicative Instanz an, für die es den ZipList ZipList- ZipList gibt:

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

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

Da zip sein Ergebnis nach der kürzesten Eingabe trimmt, ist die einzige Implementierung von pure , die die Applicative Gesetze erfüllt, eine, die eine unendliche Liste zurückgibt:

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

Zum Beispiel:

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

Die beiden Möglichkeiten erinnern uns an das äußere und das innere Produkt, ähnlich dem Multiplizieren einer 1-Spalten-Matrix ( nx 1 ) mit einer 1-Zeilen-Matrix ( 1 xm ) im ersten Fall, wobei die nxm Matrix als Ergebnis erhalten wird (jedoch abgeflacht wird ); oder Multiplizieren einer 1-Zeilen- und einer 1-Spalten-Matrix (jedoch ohne das Summieren) im zweiten Fall.

Funktionen

Bei der Spezialisierung auf Funktionen (->) r stimmen die Typunterschriften von pure und <*> mit denen der Kombinatoren K und S überein:

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

pure muss const , und <*> nimmt ein Paar von Funktionen und wendet diese jeweils auf ein festes Argument an, wobei die beiden Ergebnisse angewendet werden:

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

Funktionen sind der prototypische "Zippy" -Applikativ. Da zum Beispiel unendliche Ströme isomorph zu (->) 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))

Durch die Darstellung von Streams in einer höheren Reihenfolge wird automatisch die fließende Applicative Instanz erzeugt.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow