Haskell Language
Toepasselijke Functor
Zoeken…
Invoering
Applicative is de klasse van typen f :: * -> * waarmee een opgeheven functie kan worden toegepast op een structuur waarbij de functie ook in die structuur is ingebed.
Opmerkingen
Definitie
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Let op de Functor op f . De pure functie retourneert zijn argument ingebed in de Applicative structuur. De infix-functie <*> (uitgesproken als "toepassen") lijkt erg op fmap behalve met de functie ingebed in de Applicative structuur.
Een correct exemplaar van Applicative moet voldoen aan de applicatieve wetgeving , hoewel deze niet worden afgedwongen door de compiler:
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
Alternatieve definitie
Aangezien elke Applicative Functor een Functor is , kan er altijd fmap op worden gebruikt; de essentie van Applicative is dus het koppelen van gedragen inhoud, evenals de mogelijkheid om het te maken:
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
Deze klasse is isomorf voor Applicative .
pure a = const a <$> funit = a <$ funit
fa <*> fb = (\(a,b) -> a b) <$> fpair (fa, fb) = uncurry ($) <$> fpair (fa, fb)
Omgekeerd,
funit = pure ()
fpair (fa, fb) = (,) <$> fa <*> fb
Veel voorkomende gevallen van toepassing
Kan zijn
Maybe is een applicatief functor die een mogelijk-afwezige waarde bevat.
instance Applicative Maybe where
pure = Just
Just f <*> Just x = Just $ f x
_ <*> _ = Nothing
pure tilt de gegeven waarde in Maybe door Just te passen. De functie (<*>) past een functie gewikkeld in een Maybe op een waarde in een Maybe . Als zowel de functie als de waarde aanwezig zijn (geconstrueerd met Just ), wordt de functie op de waarde toegepast en wordt het samengevoegde resultaat geretourneerd. Als een van beide ontbreekt, kan de berekening niet doorgaan en wordt er Nothing geretourneerd.
lijsten
Een manier voor lijsten om te passen bij de typeaanduiding <*> :: [a -> b] -> [a] -> [b] is om het Cartesiaanse product van de twee lijsten te nemen, waarbij elk element van de eerste lijst aan elk wordt gekoppeld element van de tweede:
fs <*> xs = [f x | f <- fs, x <- xs]
-- = do { f <- fs; x <- xs; return (f x) }
pure x = [x]
Dit wordt meestal geïnterpreteerd als het emuleren van niet-determinisme, met een lijst met waarden die staat voor een niet-deterministische waarde waarvan de mogelijke waarden variëren over die lijst; dus een combinatie van twee niet-deterministische waarden varieert over alle mogelijke combinaties van de waarden in de twee lijsten:
ghci> [(+1),(+2)] <*> [3,30,300]
[4,31,301,5,32,302]
Oneindige streams en zip-lijsten
Er is een klasse van Applicative 's die hun twee ingangen samen "zipen". Een eenvoudig voorbeeld is dat van oneindige streams:
data Stream a = Stream { headS :: a, tailS :: Stream a }
De Applicative instantie van Stream past een stroom van functies puntsgewijs toe op een stroom argumenten, waarbij de waarden in de twee stromen per positie worden gekoppeld. pure retourneert een constante stroom - een oneindige lijst met één vaste waarde:
instance Applicative Stream where
pure x = let s = Stream x s in s
Stream f fs <*> Stream x xs = Stream (f x) (fs <*> xs)
Lijsten laten ook een "zippy" Applicative instantie toe, waarvoor het ZipList type ZipList bestaat:
newtype ZipList a = ZipList { getZipList :: [a] }
instance Applicative ZipList where
ZipList xs <*> ZipList ys = ZipList $ zipWith ($) xs ys
Aangezien zip zijn resultaat bijsnijdt volgens de kortste invoer, is de enige implementatie van pure die voldoet aan de Applicative wetgeving er een die een oneindige lijst retourneert:
pure a = ZipList (repeat a) -- ZipList (fix (a:)) = ZipList [a,a,a,a,...
Bijvoorbeeld:
ghci> getZipList $ ZipList [(+1),(+2)] <*> ZipList [3,30,300]
[4,32]
De twee mogelijkheden herinneren ons aan het buitenste en het binnenste product, vergelijkbaar met het vermenigvuldigen van een matrix met 1 kolom ( nx 1 ) met een matrix met 1 rij ( 1 xm ) in het eerste geval, waardoor de nxm matrix wordt verkregen (maar afgeplat ); of vermenigvuldigen van een 1-rij en een 1-kolom matrices (maar zonder de samenvatting) in het tweede geval.
functies
Wanneer gespecialiseerd in functies (->) r , komen de typeaanduidingen van pure en <*> overeen met die van respectievelijk de K en S combinators:
pure :: a -> (r -> a)
<*> :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
pure moet const zijn en <*> neemt een paar functies en past ze elk toe op een vast argument, waarbij de twee resultaten worden toegepast:
instance Applicative ((->) r) where
pure = const
f <*> g = \x -> f x (g x)
Functies zijn de prototypische "zippy" applicatie. Omdat oneindige stromen bijvoorbeeld isomorf zijn voor (->) 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))
... door streams op een hogere volgorde weer te geven, wordt de zippy Applicative instantie automatisch geproduceerd.