Skip to content

Commit fc4b7f8

Browse files
committed
on the way
1 parent b6a4537 commit fc4b7f8

File tree

2 files changed

+58
-43
lines changed

2 files changed

+58
-43
lines changed

app/Main.hs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,13 @@ main = do
3535
hSetEncoding stdout utf8 -- this is required to handle UTF-8 characters like λ
3636

3737
--let testSource = "main = (\\x y -> + x x) 3 4"
38-
mapM_ showCompilations [sqr, factorial] --, fibonacci, ackermann, tak]
38+
mapM_ showCompilations [prod, factorial] --, fibonacci, ackermann, tak]
3939
--demo
4040

4141
type SourceCode = String
4242

43-
sqr :: SourceCode
44-
sqr = [r|
45-
sqr = \x. * x x
46-
main = sqr 3
47-
|]
43+
prod :: SourceCode
44+
prod = "main = λx y. * x y"
4845

4946
tak :: SourceCode
5047
tak = [r|
@@ -76,18 +73,18 @@ showCompilations source = do
7673
putStrLn "The parsed environment of named lambda expressions:"
7774
mapM_ print env
7875
putStrLn ""
79-
putStrLn "The expressions in de Bruijn notation:"
76+
putStrLn "The main expression in de Bruijn notation:"
8077
mapM_ (print . Data.Bifunctor.second deBruijn) env
8178

82-
putStrLn "applying plain compilation:"
83-
print $ compilePlain env
84-
putStrLn ""
85-
8679
let expr = compile env abstractToSKI
87-
putStrLn "The main expression compiled to SICKBY combinator expressions:"
80+
putStrLn "The main expression compiled to SICKBY combinator expressions by recursice bracket abstraction:"
8881
print expr
8982
putStrLn ""
9083

84+
putStrLn "applying plain Kiselyov compilation:"
85+
print $ compilePlain env
86+
putStrLn ""
87+
9188
let expr' = compileEta env
9289
putStrLn "The main expression compiled to SICKBY combinator expressions with eta optimization:"
9390
print expr'

kiselyov.md

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -82,34 +82,30 @@ index :: Eq a => a -> [a] -> Maybe Peano
8282
index x xs = lookup x $ zip xs $ iterate Succ Zero
8383
```
8484

85-
Lets see how this works on our example `sqr` and `main` functions:
85+
Lets see how this works on a simple `main` functions:
8686

8787
```haskell
88-
let source = [r|
89-
sqr = \x. * x x
90-
main = sqr 3
91-
|]
88+
let source = "main = λx y. * x y"
9289
let env = parseEnvironment source
9390
putStrLn "The parsed environment of named lambda expressions:"
9491
mapM_ print env
9592
putStrLn ""
96-
putStrLn "The expressions in de Bruijn notation:"
93+
putStrLn "The main expression in de Bruijn notation:"
9794
mapM_ (print . Data.Bifunctor.second deBruijn) env
9895
```
9996
This will produce the following output:
10097

10198
```haskell
10299
The parsed environment of named lambda expressions:
103-
("sqr", Lam "x" (App (App (Var "*") (Var "x")) (Var "x")))
104-
("main", App (Var "sqr") (Int 3))
100+
("main",Lam "x" (Lam "y" (App (App (Var "*") (Var "x")) (Var "y"))))
105101

106-
The expressions in de Bruijn notation:
107-
("sqr", L (A (A (Free "*") (N Zero)) (N Zero)))
108-
("main", A (Free "sqr") (IN 3))
102+
The main expression in de Bruijn notation:
103+
("main",L (L (A (A (Free "*") (N (Succ Zero))) (N Zero))))
109104
```
110105

111-
It's easy to see that the de Bruijn notation is just a different representation of the λ-calculus terms. The only difference is that the variable names are replaced by indices.
112-
This is quite helpful as it allows to systematically adress variables by their respective position without having to deal with arbitrary variable names.
106+
It's easy to see that the de Bruijn notation is just a different representation of the λ-term. The only difference is that the variable names are replaced by indices.
107+
The innermost lambda-abstraction binds the variable `y` which is represented by the index `Zero`. The next lambda-abstraction binds the variable `x` which is represented by the index `Succ Zero`.
108+
This notation is quite helpful as it allows to systematically adress variables by their respective position in a complex term.
113109

114110
But why are we using Peano numbers for the indices? Why not just use integers?
115111
Well it's definitely possible to [use integers instead of Peano numbers](https://crypto.stanford.edu/~blynn/lambda/cl.html).
@@ -120,11 +116,38 @@ In the subsequent compilation steps we want to be able to do pattern matching on
120116
data Peano = Succ Peano | Zero
121117
```
122118

123-
Now we'll take a look at the next step in the compilation process. The function `convert` translates the de Bruijn notation to a data type `CL` which represents the combinator terms.
119+
Starting with the de Bruijn notation Ben Lynn's implementation of Kiselyov's algorithm builds up a series of increasingly optimized compilers that translate λ-expressions to combinator terms.
124120

121+
I'll don't want to go into all the details of the algorithm. [Ben's blog post](https://crypto.stanford.edu/~blynn/lambda/kiselyov.html) is a great resource for this. I'll just give a brief overview of the compilation results of the different compilers.
125122

123+
## The Plain compiler
124+
``` haskell
125+
compilePlain :: Environment -> CL
126+
compilePlain env = case lookup "main" env of
127+
Nothing -> error "main function missing"
128+
Just main -> snd $ plain env (deBruijn main)
129+
```
130+
131+
The first compiler is called `plain`. It is a straightforward translation of the de Bruijn notation to combinators. It uses the `CL` data type to represent combinator-terms:
132+
133+
```haskell
134+
data CL = Com Combinator | INT Integer | CL :@ CL
135+
136+
data Combinator = I | K | S | B | C | Y | R | B' | C' | S' | T |
137+
ADD | SUB | MUL | DIV | REM | SUB1 | EQL | GEQ | ZEROP
138+
deriving (Eq, Show)
139+
```
140+
141+
The `plain` function is defined as follows:
126142

127143
```haskell
144+
plain :: Environment -> DB -> (Int, CL)
145+
plain = convert (#) where
146+
(0 , d1) # (0 , d2) = d1 :@ d2
147+
(0 , d1) # (n , d2) = (0, Com B :@ d1) # (n - 1, d2)
148+
(n , d1) # (0 , d2) = (0, Com R :@ d2) # (n - 1, d1)
149+
(n1, d1) # (n2, d2) = (n1 - 1, (0, Com S) # (n1 - 1, d1)) # (n2 - 1, d2)
150+
128151
convert :: ((Int, CL) -> (Int, CL) -> CL) -> [(String, Expr)] -> DB -> (Int, CL)
129152
convert (#) env = \case
130153
N Zero -> (1, Com I)
@@ -138,29 +161,24 @@ convert (#) env = \case
138161
IN i -> (0, INT i)
139162
Free s -> convertVar (#) env s
140163
where rec = convert (#) env
141-
```
142-
143-
xxx
144164

145-
146-
147-
148-
149-
150-
151-
Combinator terms are defined as follows:
165+
-- | convert a free variable to a combinator.
166+
-- first we try to find a definition in the environment.
167+
-- if that fails, we assume it is a combinator.
168+
convertVar :: ((Int, CL) -> (Int, CL) -> CL) -> [(String, Expr)] -> String -> (Int, CL)
169+
convertVar (#) env s
170+
| Just t <- lookup s env = convert (#) env (deBruijn t)
171+
| otherwise = (0, Com (fromString s))
172+
```
152173

153174
```haskell
154-
data CL = Com Combinator | INT Integer | CL :@ CL
175+
The main expression compiled to SICKBY combinator expressions by recursice bracket abstraction:
176+
MUL
155177

156-
data Combinator = I | K | S | B | C | Y | R | B' | C' | S' | T |
157-
ADD | SUB | MUL | DIV | REM | SUB1 | EQL | GEQ | ZEROP
158-
deriving (Eq, Show)
178+
applying plain Kiselyov compilation:
179+
R I(B S(B(B MUL)(B K I)))
159180
```
160181

161-
162-
163-
164182
## to be continued...
165183

166184
## performance comparison

0 commit comments

Comments
 (0)