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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow