Skip to content

Commit c8ab7d2

Browse files
committed
Add let support in Lambda evaluator
1 parent 0e5d2f7 commit c8ab7d2

File tree

3 files changed

+60
-11
lines changed

3 files changed

+60
-11
lines changed

src/Language/Lambda.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ type Globals = Map.Map String (LambdaExpr String)
2525
evalString :: Globals
2626
-> String
2727
-> Either ParseError (LambdaExpr String, Globals)
28-
evalString globals str = flip (,) globals <$> eval'
29-
where eval' = evalExpr uniques <$> parseExpr str
28+
evalString globals str = evalExpr globals uniques <$> parseExpr str
3029

3130
uniques :: [String]
3231
uniques = concatMap (\p -> map (:p) . reverse $ ['a'..'z']) suffix

src/Language/Lambda/Eval.hs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,50 @@ module Language.Lambda.Eval where
33
import Data.List
44
import Data.Maybe
55

6+
import qualified Data.Map as Map
7+
68
import Language.Lambda.Expression
79

8-
evalExpr :: Eq n => [n] -> LambdaExpr n -> LambdaExpr n
9-
evalExpr uniqs (Abs name expr) = Abs name . evalExpr uniqs $ expr
10-
evalExpr _ expr@(Var _) = expr
11-
evalExpr uniqs (App e1 e2) = betaReduce uniqs (evalExpr uniqs e1)
12-
(evalExpr uniqs e2)
10+
-- | Evaluate an expression
11+
evalExpr :: (Eq n, Ord n)
12+
=> Map.Map n (LambdaExpr n) -- ^ globals
13+
-> [n] -- ^ unique supply
14+
-> LambdaExpr n -- ^ the expression to evaluate
15+
-> (LambdaExpr n, Map.Map n (LambdaExpr n))
16+
evalExpr globals uniqs (Let name expr)
17+
= (Let name expr', Map.insert name expr' globals)
18+
where expr' = evalExpr' uniqs (subGlobals globals expr)
19+
evalExpr globals uniqs expr = (evalExpr' uniqs expr', globals)
20+
where expr' = subGlobals globals expr
1321

14-
betaReduce :: Eq n => [n] -> LambdaExpr n -> LambdaExpr n -> LambdaExpr n
15-
betaReduce uniqs (App e1 e1') e2 = App (betaReduce uniqs e1 e1') e2
22+
subGlobals :: (Eq n, Ord n)
23+
=> Map.Map n (LambdaExpr n) -- ^ globals
24+
-> LambdaExpr n -- ^ the expression
25+
-> LambdaExpr n
26+
subGlobals g e@(Var x) = Map.findWithDefault e x g
27+
subGlobals g (App e1 e2) = App (subGlobals g e1) (subGlobals g e2)
28+
subGlobals g (Abs n expr) = Abs n (subGlobals g expr)
29+
subGlobals _ expr = expr
30+
31+
-- | Evaluate an expression; does not support `let`
32+
evalExpr' :: Eq n
33+
=> [n] -- ^ unique supply
34+
-> LambdaExpr n -- ^ the expression to evaluate
35+
-> LambdaExpr n
36+
evalExpr' _ expr@(Var _) = expr
37+
evalExpr' uniqs (Abs name expr) = Abs name . evalExpr' uniqs $ expr
38+
evalExpr' uniqs (Let name expr) = Let name . evalExpr' uniqs $ expr
39+
evalExpr' uniqs (App e1 e2) = betaReduce uniqs (evalExpr' uniqs e1)
40+
(evalExpr' uniqs e2)
41+
42+
betaReduce :: Eq n
43+
=> [n]
44+
-> LambdaExpr n
45+
-> LambdaExpr n
46+
-> LambdaExpr n
1647
betaReduce _ expr@(Var _) e2 = App expr e2
17-
betaReduce uniqs (Abs n e1) e2 = evalExpr uniqs . sub n e1' $ e2
48+
betaReduce uniqs (App e1 e1') e2 = App (betaReduce uniqs e1 e1') e2
49+
betaReduce uniqs (Abs n e1) e2 = evalExpr' uniqs . sub n e1' $ e2
1850
where fvs = freeVarsOf e2
1951
e1' = alphaConvert uniqs fvs e1
2052

test/Language/Lambda/EvalSpec.hs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Language.Lambda.EvalSpec where
22

3+
import Data.Map (empty, insert)
34
import Test.Hspec
45

56
import Language.Lambda
@@ -9,7 +10,7 @@ import Language.Lambda.Expression
910
spec :: Spec
1011
spec = do
1112
describe "evalExpr" $ do
12-
let evalExpr' = evalExpr uniques
13+
let evalExpr' = fst <$> evalExpr empty uniques
1314

1415
it "beta reduces" $ do
1516
let expr = App (Abs "x" (Var "x")) (Var "z")
@@ -28,6 +29,23 @@ spec = do
2829
(Abs "f" (Var "x"))
2930
evalExpr' expr `shouldBe` Abs "z" (Var "x")
3031

32+
it "reduces let bodies" $ do
33+
let expr = Let "x" $ App (Abs "y" (Var "y")) (Var "z")
34+
evalExpr' expr `shouldBe` Let "x" (Var "z")
35+
36+
it "let expressions update state" $ do
37+
let expr = Let "w" (Var "x")
38+
(_, globals) = evalExpr empty uniques expr
39+
40+
globals `shouldBe` insert "w" (Var "x") empty
41+
42+
it "subs global variables" $ do
43+
let globals = insert "w" (Var "x") empty
44+
expr = Var "w"
45+
46+
fst (evalExpr globals uniques expr)
47+
`shouldBe` Var "x"
48+
3149
describe "betaReduce" $ do
3250
let betaReduce' = betaReduce []
3351

0 commit comments

Comments
 (0)