Haskell Language
Tillämplig Functor
Sök…
Introduktion
Applicative
är klassen av typerna f :: * -> *
som tillåter lyftad funktionstillämpning över en struktur där funktionen också är inbäddad i den strukturen.
Anmärkningar
Definition
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Observera Functor
begränsningen på f
. Den pure
funktionen returnerar sitt argument inbäddat i Applicative
. Infixfunktionen <*>
(uttalas "applicera") är mycket lik fmap
förutom med funktionen inbäddad i Applicative
.
En korrekt instans av Applicative
bör uppfylla tillämpliga lagar , även om dessa inte verkställs av kompilatorn:
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
Alternativ definition
Eftersom varje tillämplig funktor är en funktor , kan fmap
alltid användas på den; följaktligen är kärnan i tillämplig parning av transporterat innehåll samt möjligheten att skapa det:
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
Denna klass är isomorf för Applicative
.
pure a = const a <$> funit = a <$ funit
fa <*> fb = (\(a,b) -> a b) <$> fpair (fa, fb) = uncurry ($) <$> fpair (fa, fb)
Omvänt,
funit = pure ()
fpair (fa, fb) = (,) <$> fa <*> fb
Vanliga instanser av tillämpligt
Kanske
Maybe
är en tillämplig funktor som innehåller ett eventuellt frånvarande värde.
instance Applicative Maybe where
pure = Just
Just f <*> Just x = Just $ f x
_ <*> _ = Nothing
pure
lyfter upp det givna värdet till Maybe
genom att applicera Just
på det. (<*>)
-Funktionen tillämpar en funktion insvept i en Maybe
till ett värde i en Maybe
. Om både funktionen och värdet finns (konstruerat med Just
) tillämpas funktionen på värdet och det inslagna resultatet returneras. Om endera saknas kan beräkningen inte fortsätta och Nothing
returneras istället.
listor
Ett sätt för listor att anpassa till typsignaturen <*> :: [a -> b] -> [a] -> [b]
är att ta de två listorna: s kartesiska produkt, koppla ihop varje element i den första listan med varje del av den andra:
fs <*> xs = [f x | f <- fs, x <- xs]
-- = do { f <- fs; x <- xs; return (f x) }
pure x = [x]
Detta tolkas vanligtvis som emulering av nondeterminism, med en lista över värden som står för ett nondeterministiskt värde vars möjliga värden sträcker sig över den listan; så en kombination av två nondeterministiska värden sträcker sig över alla möjliga kombinationer av värdena i de två listorna:
ghci> [(+1),(+2)] <*> [3,30,300]
[4,31,301,5,32,302]
Oändliga strömmar och zip-listor
Det finns en klass av Applicative
som "zip" sina två ingångar tillsammans. Ett enkelt exempel är det av oändliga strömmar:
data Stream a = Stream { headS :: a, tailS :: Stream a }
Stream
s Applicative
instans applicerar en ström av funktioner för en ström av argument pekar-wise, para upp värdena i de två strömmarna av position. pure
ger en konstant ström - en oändlig lista med ett enda fast värde:
instance Applicative Stream where
pure x = let s = Stream x s in s
Stream f fs <*> Stream x xs = Stream (f x) (fs <*> xs)
Listor medger också en "zippy" Applicative
instans, för vilken det finns en ZipList
typ av ZipList:
newtype ZipList a = ZipList { getZipList :: [a] }
instance Applicative ZipList where
ZipList xs <*> ZipList ys = ZipList $ zipWith ($) xs ys
Eftersom zip
trimmar resultatet enligt den kortaste inmatningen, är den enda implementeringen av pure
som uppfyller Applicative
lagar en som ger en oändlig lista:
pure a = ZipList (repeat a) -- ZipList (fix (a:)) = ZipList [a,a,a,a,...
Till exempel:
ghci> getZipList $ ZipList [(+1),(+2)] <*> ZipList [3,30,300]
[4,32]
De två möjligheterna påminner oss om den yttre och den inre produkten, liknande att multiplicera en 1-kolonn ( nx 1
) matris med en 1-rad ( 1 xm
) i första fallet, att få nxm
matrisen som ett resultat (men plattad) ); eller multiplicera en 1-rads- och en-kolumnmatris (men utan summering) i det andra fallet.
funktioner
När de är specialiserade på funktioner (->) r
matchar typsignaturerna av pure
och <*>
signalerna från K
och S
kombinationerna:
pure :: a -> (r -> a)
<*> :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
pure
must be const
, och <*>
tar ett par funktioner och tillämpar dem var och en på ett fast argument och tillämpar de två resultaten:
instance Applicative ((->) r) where
pure = const
f <*> g = \x -> f x (g x)
Funktioner är den prototypiska "zippy" tillämpning. Eftersom oändliga strömmar till exempel är isomorfa för (->) 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))
... som representerar strömmar på ett högre sätt producerar zippy Applicative
instans automatiskt.