2014-06-30 15 views
5

Sto imparando Haskell con la guida Learn You a Haskell e sono bloccato su un esempio di sequenziamento applicativo su un elenco di funzioni. Chapter 11: Functors, Applicative Functors and Monoids definisce sequenceA essere:Come funziona il sequenziamento applicativo su un elenco di funzioni?

sequenceA :: (Applicative f) => [f a] -> f [a] 
sequenceA [] = pure [] 
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs 

Sono un po 'confuso da questo esempio di utilizzo di sequenceA:

> sequenceA [(+3),(+2),(+1)] 3 
[6,5,4] 

ho ampliato l'applicazione manualmente, per quanto ho potuto per quella che credo sia corretto:

(:) <$> (+3) <*> sequenceA [(+2), (+1)] 
(:) <$> (+3) <*> (:) <$> (+2) <*> (:) <$> (+1) <*> sequenceA [] 
(:) <$> (+3) <*> (:) <$> (+2) <*> (:) <$> (+1) <*> pure [] 
(:) <$> (+3) <*> (:) <$> (+2) <*> (:) <$> (+1) <*> const [] 

Quello che non vedo però, è come il secondo argomento della domanda originale di sequenceA, 3 viene applicato a ciascuno dei parzialmente applie d funzioni indicate nell'elenco. Gradirei un po 'di aiuto nel cercare di capire cosa succede nella valutazione di questa affermazione.

+4

Suggerimento: L'istanza applicativo è '(->) a'. Elabora l'applicazione 'fmap' /' <*> '! – Xeo

risposta

10

Ogni volta che hai a che fare con i tipi generici è necessario determinare prima che cosa la il tipo concreto è. In questo caso, abbiamo

sequenceA :: Applicative f => [f a] -> f [a] 

applicato a

[(+3),(+2),(+1)] :: Num a => [a -> a] 

In altre parole, anche se è un po 'strano vedere in un primo momento, f diventa a -> (correttamente scritto (->) a), e così il tipo di sequenceA , specializzato, è

sequenceA :: Num a => [a -> a] -> a -> [a] 

Quale dovrebbe già spiegare da dove è venuto questo argomento extra.


Quindi, come funziona sequenceA? Per capire, dobbiamo capire l'istanza Applicative per (->) a. In particolare

instance Functor ((->) a) where 
    fmap f g = f . g 

instance Applicative ((->) a) where 
    pure a' = \a -> a' 
    ff <*> fx = \a -> (ff a) (fx a) 

dove si vede che (<*>) prende due funzioni e produce un terzo. L'argomento di questa terza funzione viene applicato a ciascuna delle funzioni di input (qui, ff e fx) e quindi i risultati vengono applicati l'uno all'altro.

Quindi l'applicazione nel monad (->) a significa che l'argomento finale a al risultato viene distribuito a tutte le funzioni di composizione.

e questo è ciò che vediamo con sequenceA

sequenceA [(+3),(+2),(+1)] 
== 
\a -> [(a + 3), (a + 2), (a + 1)] 
+0

Grazie per la spiegazione molto utile, J! :) Credo di avere qualcosa dopo aver giocato con ghci per un po ': '> ((:). (+3) $ 3) (((:) (+2) $ 3) (((:) . (+1) $ 3) (const [] 3))) ' ' [6,5,4] ' Anche se devo ammettere, non riesco ancora a capire come viene distribuito il 3. Vedo che l'operatore infix fmap ha una funzione per prendere il 3, ma è difficile per me immaginare che venga effettivamente inviato a tutte le funzioni. Forse c'è un modo migliore per spiegarlo, o in caso contrario, si spera che la sua comprensione sia accompagnata da più pratica. – bpaterni

+0

Forse un ulteriore studio su 'sequenceA' sarebbe utile allora. Prova a dare un'occhiata ad altri applicativi o monadi ('Reader' sarà importante!). In parole povere, 'sequenceA' prende diversi calcoli efficaci e" condivide "i loro effetti in sequenza. Quindi il nome. Per la sequenza di applicazione delle funzioni non importa, ma la condivisione lo fa. –

1

Usando:

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

si può ricavare:

 (:) <$> (+3) <*> (  (:) <$> (+2) <*> ( (:) <$> (+1) <*> const [])) 
pure (:) <*> (+3) <*> (pure (:) <*> (+2) <*> (pure (:) <*> (+1) <*> const [])) 
const (:) <*> (+3) <*> (const (:) <*> (+2) <*> (const (:) <*> (+1) <*> const [])) 
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> ((const (:) <*> (+1)) <*> const [])) 
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> ((\x -> (const (:)) x (x+1)) <*> const [])) 
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> ((\x -> (:) (x+1)) <*> const [])) 
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> (\y -> (\x -> (:) (x+1)) y (const [] y))) 
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> (\y -> (\x -> (:) (x+1)) y [])) 
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> (\y -> ((y+1):) [])) 
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> (\y -> [y+1])) 
(const (:) <*> (+3)) <*> (((\x -> (const (:)) x (x+2)) <*> (\y -> [y+1])) 

(const (:) <*> (+3)) <*> (\z -> ((\x -> (const (:)) x (x+2)) z ((\y -> [y+1])z)) 
(const (:) <*> (+3)) <*> (\z -> (((const (:)) z (z+2)) ([z+1])) 
(const (:) <*> (+3)) <*> (\z -> ((z+2):) [z+1]) 
(const (:) <*> (+3)) <*> (\z -> [z+2,z+1]) 
(\x -> (const (:)) x (x+3)) <*> (\z -> [z+2,z+1]) 
(\x -> const (:) (x+3)) <*> (\z -> [z+2,z+1]) 
(\x -> ((x+3):)) <*> (\z -> [z+2,z+1]) 
\w -> (\x -> ((x+3):)) w ((\z -> [z+2,z+1]) w) 
\w -> ((w+3):) [w+2,w+1] 
\w -> [w+3,w+2,w+1] 

Scommetto che ho incasinato alcune parentesi

Problemi correlati