Haskell Language
Anwendungsfähiger Functor
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.