#include "head.html"
Erlaubt sein soll
let { f x = x + x } in f 0Wir ändern dazu zunächst die abstrakte Syntax
#include "../src/Function_Type.hs"dann entsprechend deren grafische Darstellung (Quelltext) sowie schließlich die konkrete Syntax (den Parser) (Quelltext). Die wesentliche Änderung dabei ist, daß dort, wo wir früher nur einen Namen lasen, jetzt eine Folgen von Namen (links vom "=") bzw. ein Name und dann eine Folge von Termen erlaubt sind:
bindung :: Parser Term bindung = do keyword "let" nts <- brace $ sepBy (operator ";") $ do n <- name' params <- many name' operator "=" t <- term return (n, params, t) keyword "in" body <- term return $ Let nts bodyWir merken uns jetzt also in Let-Umgebungen nicht Bindungen der Variablen an fertige Werte, sondern an (unausgewertete) Terme, und zudem noch die Namen der formalen Parameter, damit wir beim Aufruf die passende Umgebung zusammensetzen können. Das sieht dann so aus:
#include "../src/Function_Eval.hs"und jetzt haben wir die Äquivalenz zwischen
let { x = 3 | 4 } in x * x und (3|4) * (3|4)
let { e n = n + 1; z n = e (e n) } in z 0Warum stürzt das ab (d. h. bleibt klemmen)? Hat offenbar was mit der doppelten Benutzung des n zu tun. Das ist aber sehr ärgerlich, für uns als Mathematiker sollte es solche Beschränkungen nicht geben. Namen von rein lokalen Variablen sollten nichts an der Semantik ändern.
Sehen wir uns die Auswertung an:
z 0 [ z = \ n -> e (e n), e = \ n -> n + 1 ] e (e n) [ n = 0, z = .., e = \ n -> n + 1 ] n + 1 [ n = e n, n = 0, z = .., e = .. ] n [ n = e n, n = 0, z = .., e = .. ] e n [ n = e n, n = 0, z = .., e = \ n -> n + 1 ]Hier ist es passiert: es hätte e 0 dort stehen sollen, aber die richtige Bindung ist verdeckt. Mit umbenannten Variablen geht es:
z 0 [ z = \ n -> e (e n), e = \ k -> k + 1 ] e (e n) [ n = 0, z = .., e = \ k -> k + 1 ] k + 1 [ k = e n, n = 0, z = .., e = .. ] k [ k = e n, n = 0, z = .., e = .. ] e n [ k = e n, n = 0, z = .., e = \ n -> n + 1 ]
Um das zu implementieren, müssen wir den Interpreter erweitern: nicht Function = ([String], Term), sondern Function = ([String], Term, Envs Function). Das geht aber mit Typ-Synonymen nicht (diese dürfen nicht rekursiv sein), sondern nur mit algebraischen Datentypen.
#include "../src/Static_Eval.hs"
let { e n = n + 1} in let { z n = e (e n) } in z 0denn die Bindungen innerhalb eines let sehen sich gegenseitig (und auch selbst) nicht. (Das wird es jetzt, bei der korrekten Implementierung, klar). Wir retten das wie folgt:
Let binds body -> let act = mkEnv [ (f, Function (ps, b, envs')) | (f, ps, b) <- binds ] envs' = act : envs in eval envs' bodyDas sieht sehr nach "an den eigenen Haaren aus dem Sumpf ziehen" aus, ist aber gar nicht so schlimm: Haskell selbst hat wegen der lazy evaluation nichts gegen rekursive Bindungen. #include "foot.html"