2012-01-20 26 views
16

Dato:Perché questo codice Haskell viene compilato?

uncurry :: (a-> b -> c) -> (a,b) -> c  
id :: a -> a 

Invocare uncurry id risultati in funzione del tipo: (b -> c, b) -> c

Come si arriva a questo risultato?

Come si può usare id (a -> a) come primo parametro a uncurry, che richiede un (a -> c -> b) la funzione?

risposta

25

E 'più facile capire se cerchiamo di vedere le cose dal punto di rendendo i tipi funzionano: capire cosa dobbiamo fare per tipo id s' per farlo per adattarsi alla forma richiesta dalla uncurry. Dal momento che abbiamo:

id :: a -> a 

abbiamo anche:

id :: (b -> c) -> (b -> c) 

Questo può essere visto sostituendo b -> c per a nel tipo originale di id, proprio come si potrebbe sostituire Int invece quando capire il tipo di di id 42. Possiamo quindi cadere le parentesi sul lato destro, poiché (->) è giusto-associativa:

id :: (b -> c) -> b -> c 

mostrando che tipo id s' inserisce la forma a -> b -> c, dove a è b -> c. In altre parole, siamo in grado di rimodellare il tipo s' id per adattarsi alla forma richiesta semplicemente specializzata tipo generale che ha già.

Un altro modo per capire questo è vedere che uncurry ($) ha anche il tipo (b -> c, b) -> c. Confrontando le definizioni di id e ($):

id :: a -> a 
id a = a 

($) :: (a -> b) -> a -> b 
($) f x = f x 

possiamo fare quest'ultimo definizione più punti libera:

($) f = f 

a questo punto il fatto che ($) è semplicemente una specializzazione di id ad una più specifica il tipo diventa chiaro.

+2

Grazie! Inizia a dare un senso! Lo trovo ancora un po 'spinout! – ssanj

+1

@ssanj: Se c'è qualche consolazione, scrivendo questo come "id non corretto" piuttosto che "uncurry ($)" è male :) – ehird

+1

E se vuoi essere particolarmente malvagio, potresti scrivere '(\' id \ '3) 'invece di' ($ 3) 'o definisci' id' come 'infixr 0 \' id \ '' e usalo al posto di '($)', come '(^ 2) \' id \ '1 + 2 * 3'. – Vitus

8

Come si può usare id (a -> a) come il primo parametro da mantenere, che richiede una funzione (a -> b -> c)?

realtà, uncurry richiede (a -> (b -> c)) funzione. Riesci a individuare la differenza? :)

L'omissione delle parentesi è malvagia (bene, a volte). Rende impossibile a un principiante decifrare Haskell. Ovviamente dopo aver raccolto qualche esperienza con la lingua, ti senti come se non avessi più bisogno di loro.

Qui, tutto diventa chiaro una volta che scriviamo tutte le parentesi omessi indietro in modo esplicito:

uncurry :: (a -> (b -> c)) -> ((a,b) -> c) 
id  :: a -> a 

Ora, scrivendo uncurry id chiede un tipo di unificazione a1 -> a1 con a2 -> (b -> c). Questo è semplice, a1 ~ a2 e a1 ~ (b -> c). Solo materiale meccanico, , nessun pensiero creativo ha coinvolto qui. Quindi id in questione ha effettivamente il tipo a -> a where a ~ (b -> c) e quindi uncurry id ha il tipo (b -> c,b) -> c, con la semplice sostituzione di a ~ (b -> c) in (a,b) -> c. Vale a dire, prevede una coppia di una funzione b -> c e un valore b e deve produrre un valore c.

Dal momento che i tipi sono più generale (vale a dire non si sa nulla di loro, e quindi non c'è nessun funzioni specifiche per chiamare che potrebbe fare il trucco in qualche modo speciale), il unico modo per produrre un valore c qui è quello di chiama la funzione b -> c con il valore b come argomento. Naturalmente, questo è ciò che fa ($). Quindi uncurry id == uncurry ($), anche se id è sicuramente non($).

+0

'uncurry f (x, y) = f x y; id z = z; id uncurry (x, y) = id x y = x y; id incurante = \ (x, y) -> x y; id uncurry :: (a -> b, a) -> b'. –

Problemi correlati