2015-07-01 15 views
10

From the chapter on Functors in Learn You a Haskell for Great Good, Lipovača afferma:

"Quando facciamo (+) <$> (+3) <*> (*100), stiamo facendo una funzione che utilizzerà + su i risultati di (+3) e (*100) e restituiscono questo. per dimostrare su un vero e proprio esempio, quando abbiamo fatto (+) <$> (+3) <*> (*100) $ 5, il 5 arrivati ​​applicato a (+3) e (*100), con conseguente 8 e 500. Poi, + viene chiamato con 8 e 500, res ulting in 508. "

Tuttavia, se provo a valutare la funzione me stesso, considerando questa definizione per applicativo sul funtore ((->) r):

instance Applicative ((->) r) where 
    pure x = (\_ -> x) 
    f <*> g = \x -> f x (g x) 

ho letto la valutazione della espressione di cui sopra come:

(\x -> (3 + x) (100 * x)) $ 5

Ma non vedo come si possa comporre due funzioni binarie parzialmente applicate come una singola lambda (in realtà, GHCi genera un errore di tipo infinito tentativo di legare questo a una variabile). Inoltre, ad un'interpretazione lavorare, se consideriamo la definizione tipo per <$> otteniamo:

(<$>) :: Functor f => (a -> b) -> f a -> f b

o più specificamente possiamo osservare suo sollevamento come:

(<$>) :: Functor f => (a -> b) -> (f a -> f b)

Considerando che il nostro funtore in questo caso è ((->) r), posso dedurre che questo è ciò che la trasformazione ha luogo nella valutazione precedente (supponendo che l'associatività sinistra avvenga per prima, invece della giusta applicazione associativa di 5):

(\x -> a + b) dove a = (+ 3) e b = (* 100). Questa è la funzione che dovrebbe essere restituita. Tuttavia, ho ragione nel ritenere che questa sia la forma finale (approssimativa)?

(\x -> (3 + x) + (100 * x)) $ 5

... che produce 508.

trovo descrizione di Lipovača più comprensibile in termini di come funziona l'espressione, ma il mio istinto mi dice non è del tutto vero per i dettagli Gorey sotto la Cappuccio del compilatore Haskell. È più facile per me pensare che la fmap di (+) abbia avuto come prima conseguenza una funzione con due funtori che sono parzialmente applicate a funzioni che prendono un input condiviso, e quindi abbiamo applicato un valore ad esso. Possiamo farlo a causa di una valutazione pigra. È sbagliato?

+1

"ho letto la valutazione della espressione di cui sopra come:' (\ x -> (3 + x) (100 * x)) $ 5' ". Suggerimento: la tua valutazione manca il '((+) <$>)', ergo l'errore di tipo. – duplode

+0

Sì, è per questo che continuo a includere nella seconda metà. – RJS

+0

Oops - non importa, avevo letto male la parte centrale della domanda. – duplode

risposta

15

In primo luogo, notare che sia <$> sia <*> associati a sinistra. Non c'è nulla di magico che accade internamente e possiamo vedere la trasformazione essenzialmente con una serie di espansioni di eta e riduzioni di beta.Step-by-step, sembra che questo:

(((+) <$> (+3))   <*> (*100)) $ 5  -- Add parens 
((fmap (+) (+3))  <*> (*100)) $ 5  -- Prefix fmap 
(((+) . (+3))   <*> (*100)) $ 5  -- fmap = (.) 
((\a -> (+) ((+3) a)) <*> (*100)) $ 5  -- Definition of (.) 
((\a -> (+) (a+3))  <*> (*100)) $ 5  -- Infix + 
((\a b -> (+) (a+3) b)) <*> (*100)) $ 5  -- Eta expand 
(\x -> (\a b -> (+) (a+3) b) x ((*100) x)) $ 5 -- Definition of (<*>) 
(\x -> (\a b -> (+) (a+3) b) x (x*100)) $ 5 -- Infix * 
(\a b -> (+) (a + 3) b) 5 (5*100)    -- Beta reduce 
(\a b -> (a + 3) + b) 5 (5*100)    -- Infix + 
(5 + 3) + (5*100)        -- Beta reduce (twice) 
508           -- Definitions of + and * 

Un po 'confusamente, il fatto che $ associa a destra ha meno a che fare con quello che sta succedendo qui che il fatto che la sua fissità è 0. Possiamo vedere questo se definiamo un nuovo operatore:

(#) :: (a -> b) -> a -> b 
f # a = f a 
infixl 0 # 

e in GHCi:

λ> (+) <$> (+3) <*> (*100) # 5 
508 
+0

Grazie mille per questo, questo è esattamente quello che stavo cercando. Dovrò approfondire l'espansione e la riduzione di Eta e Beta per la lettura futura. Anche se '$' è associativo giusto, viene applicato a un thunk (è sicuro chiamare tutti gli eventi di mapping rimasti di $ a thunk?) E quindi tutto questo viene valutato, giusto? – RJS

+0

@RJS Si può pensare a 'f $ a'" che si comporta come "' (f) (a) 'dove' f' e' a' sono bit di codice che possono avere spazi (non è proprio così tutto il tempo , specialmente quando '$' s sono incatenati, ma spesso lo penso in questo modo). Ho aggiunto un po 'di fissità, probabilmente dovrei espanderlo. C'è in realtà un (abbastanza buono) argomento per rendere '$' sinistra associativo, per inciso. Non sono sicuro di come sia collegato un thunk. Che cosa vuoi dire con questo? –

+0

Ah, semantica sbagliata da parte mia; una cattiva ipotesi che tutti gli argomenti non valutati siano i thunk è tutto. – RJS

Problemi correlati