2016-01-31 3 views
6

Betrachten Sie die folgenden Haskell Definitionen, genommen von this excellent Haskell video on YouTube:Wie Schwedisch ist eine sehr sehr schwedische Begrüßung?

import Data.List 
greeting = "Hello" 
swedish = intersperse 'f' 
very f x = f (f (f x)) 

Wenn wir sie in GHCi laden, sehen wir folgende Ergebnisse:

ghci> swedish greeting 
"Hfeflflfo" 
ghci> very swedish greeting 
"Hfffffffeffffffflffffffflfffffffo" 
ghci> very very swedish greeting 
"Hffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 
fffffffffffffffffffffffffffff... (536,870,913 chars total) 

Die ersten beiden Ausgänge I perfekt verstehen. Eine swedish greeting kommt mit f s durcheinander, und eine very swedish greeting ist nur eine , die dreifach durchsetzt.

Aber was genau passiert in der dritten Eingabezeile? Mein (ziemlich unvollständiges) Verständnis der Haskell-Syntax besagt, dass eine durch Leerzeichen getrennte Sequenz von Ausdrücken als Funktionsaufruf interpretiert wird, wobei der erste Ausdruck die Funktion und der Rest die Argumente sind. Wie wird in diesem Fall das äußerste very mit drei Argumenten (very, swedish und greeting) aufgerufen, wenn es nur definiert ist, um zwei zu akzeptieren? Wenn es hilft, scheint es, dass ein very very swedish greeting einem swedish $ swedish $ swedish $ swedish $ ... (27 layers of swedish) ... $ swedish $ swedish greeting entspricht.

+0

moderne schweden begrüßen "Hallo"! – epsilonhalbe

Antwort

11

Du hast gesagt, angewendet werden:

Meine (eher unvollständig) Verständnis von Haskell Syntax sagt, dass Eine durch Leerzeichen getrennte Sequenz von Ausdrücken wird als Funktionsaufruf interpretiert, wobei der erste Ausdruck die Funktion und der Rest die Argumente sind.

Sie haben Recht, dass dies kein vollständiges Verständnis dessen ist, was tatsächlich vor sich geht. Von Ihrem Beispiel:

very very swedish greeting 

Dies ist das gleiche wie:

((very very) swedish) greeting 

Dies liegt daran, Funktionsanwendung assoziative gelassen wird. Darüber hinaus nimmt jede Funktion in Haskell eine Eingabe und gibt ein Ergebnis zurück.Was Sie als Funktionen betrachten, die mehrere Eingaben akzeptieren, sind in Wirklichkeit Funktionen, die eine einzelne Eingabe akzeptieren und als Ergebnis eine Funktion zurückgeben.

Dies erklärt auch, warum Pfeile (->) die Funktionseingänge und in Funktionstypen abgrenzen. Betrachten Sie die Art von ++:

(++) :: [a] -> [a] -> [a] 

Dies ist das gleiche wie:

(++) :: [a] -> ([a] -> [a]) 

Sie mögen denken, der ++ Operator als zwei Listen zu nehmen und eine Liste zurückkehren, aber in Wirklichkeit ist es eine Funktion von eine Eingabe (eine Liste), die eine Funktion einer Eingabe (eine andere Liste) zurückgibt, die eine Liste zurückgibt.

Zusammengenommen können Sie hoffentlich sehen, dass very very ein gültiger Ausdruck ist (und zufälligerweise denselben Typ wie very hat).

very very x 

entspricht:

very (very (very x)) 
+0

Gibt es einen Grund, warum 'sehr sehr x' keine Endlosschleife erzeugt? –

3

Funktionsanwendung ist assoziativ links, so

very very swedish greeting 

entspricht

((very very) swedish) greeting 

very(t -> t) -> t -> t hat geben. very kann als das erste Argument very

very  :: (t  -> t  ) -> t  -> t 
    very :: (t -> t) -> (t -> t) 
very very ::       (t -> t) -> (t -> t) 

very very ist auch eine Funktion übergeben werden, hat es den Typ (t -> t) -> t -> t. Da swedish den Typ String -> String hat, kann es an very very übergeben werden. Die sich ergebende Funktion hat String -> String

((very very) swedish) :: String -> String 

Eine Funktion mit Typ-Typ String -> String kann greeting :: String

(((very very) swedish) greeting) :: String