Skip to content

Commit

Permalink
making progress
Browse files Browse the repository at this point in the history
  • Loading branch information
thma committed Oct 3, 2023
1 parent fc4b7f8 commit d64d84a
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 63 deletions.
15 changes: 11 additions & 4 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Kiselyov
import System.TimeIt
import Text.RawString.QQ
import qualified Data.Bifunctor
import LambdaToSKI (compileBracket)


printGraph :: ST s (STRef s (Graph s)) -> ST s String
Expand All @@ -35,7 +36,7 @@ main = do
hSetEncoding stdout utf8 -- this is required to handle UTF-8 characters like λ

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

type SourceCode = String
Expand All @@ -46,7 +47,7 @@ prod = "main = λx y. * x y"
tak :: SourceCode
tak = [r|
tak = y(λf x y z. (if (geq y x) z (f (f (sub1 x) y z) (f (sub1 y) z x) (f (sub1 z) x y ))))
main = tak 7 4 2 --18 6 3
main = tak 7 4 2
|]

ackermann :: SourceCode
Expand Down Expand Up @@ -76,7 +77,7 @@ showCompilations source = do
putStrLn "The main expression in de Bruijn notation:"
mapM_ (print . Data.Bifunctor.second deBruijn) env

let expr = compile env abstractToSKI
let expr = compileBracket env
putStrLn "The main expression compiled to SICKBY combinator expressions by recursice bracket abstraction:"
print expr
putStrLn ""
Expand All @@ -85,8 +86,14 @@ showCompilations source = do
print $ compilePlain env
putStrLn ""

let exprK = compileK env
putStrLn "The main expression compiled to SICKBY combinator expressions with K-optimization:"
print exprK
putStrLn ""


let expr' = compileEta env
putStrLn "The main expression compiled to SICKBY combinator expressions with eta optimization:"
putStrLn "The main expression compiled to SICKBY combinator expressions with Eta-optimization:"
print expr'
putStrLn ""

Expand Down
18 changes: 9 additions & 9 deletions benchmark/ReductionBenchmarks.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module ReductionBenchmarks where

import Criterion.Main ( defaultMain, bench, nf )
import Parser ( parseEnvironment, Expr(Int, App) )
import LambdaToSKI ( abstractToSKI, compile )
import LambdaToSKI ( compileBracket )
import CLTerm
import Kiselyov ( compileBulk, compileEta )
import GraphReduction ( allocate, normalForm, toString, Graph )
Expand All @@ -16,7 +16,7 @@ import BenchmarkSources
loadTestCase :: SourceCode -> IO CL
loadTestCase src = do
let pEnv = parseEnvironment src
expr = compile pEnv abstractToSKI
expr = compileBracket pEnv
return expr

loadTestCaseBulk :: SourceCode -> IO CL
Expand Down Expand Up @@ -104,13 +104,13 @@ benchmarks = do
, bench "ackermann HHI-Bulk" $ nf reducerTest akkBulk
, bench "ackermann HHI-Bulk-Log" $ nf reducerTestLog akkBulk
, bench "ackermann Native" $ nf ack_2 2
, bench "gaussian Graph-Reduce" $ nf graphTest gau
, bench "gaussian Graph-Reduce-Eta" $ nf graphTest gauEta
, bench "gaussian HHI-Reduce" $ nf reducerTest gau
, bench "gaussian HHI-Eta" $ nf reducerTest gauEta
, bench "gaussian HHI-Bulk" $ nf reducerTest gauBulk
, bench "gaussian HHI-Bulk-Log" $ nf reducerTestLog gauBulk
, bench "gaussian Native" $ nf gaussianSum 100
-- , bench "gaussian Graph-Reduce" $ nf graphTest gau
-- , bench "gaussian Graph-Reduce-Eta" $ nf graphTest gauEta
-- , bench "gaussian HHI-Reduce" $ nf reducerTest gau
-- , bench "gaussian HHI-Eta" $ nf reducerTest gauEta
-- , bench "gaussian HHI-Bulk" $ nf reducerTest gauBulk
-- , bench "gaussian HHI-Bulk-Log" $ nf reducerTestLog gauBulk
-- , bench "gaussian Native" $ nf gaussianSum 100
, bench "tak Graph-Reduce" $ nf graphTest tak
, bench "tak Graph-Reduce-Eta" $ nf graphTest takEta
, bench "tak HHI-Reduce" $ nf reducerTest tak
Expand Down
188 changes: 139 additions & 49 deletions kiselyov.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type Environment = [(String, Expr)]
```

Now we can define a compiler that translates such λ-expressions to combinator terms.
(You will find the complete code in [Kiselyov.hs](https://github.com/thma/lambda-ski/blob/main/src/Kiselyov.hs)

Our journey begins by translating λ-expressions to a data type `DB` which is quite similar to the λ-calculus terms but uses indices instead of variable names. This is done by the function `deBruijn`:

Expand Down Expand Up @@ -108,81 +109,170 @@ The innermost lambda-abstraction binds the variable `y` which is represented by
This notation is quite helpful as it allows to systematically adress variables by their respective position in a complex term.

But why are we using Peano numbers for the indices? Why not just use integers?
Well it's definitely possible to [use integers instead of Peano numbers](https://crypto.stanford.edu/~blynn/lambda/cl.html).
Well it's definitely possible to [use integers as indices](https://crypto.stanford.edu/~blynn/lambda/cl.html).
But there is a good reason to use Peano numbers in our case:
In the subsequent compilation steps we want to be able to do pattern matching on the indices. This is not possible with integers but it is possible with Peano numbers, because they are defined as an algebraic data type:
In the subsequent compilation steps we want to be able to do pattern matching on the indices. This is possible with Peano numbers, because they are defined as an algebraic data type:

```haskell
data Peano = Succ Peano | Zero
```

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.
Starting with the de Bruijn notation Ben Lynn's implementation of Kiselyov's algorithm builds up a series of six increasingly optimized compilers that translate λ-expressions to combinator terms:

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.
- a plain compiler without any optimizations (`compilePlain`)
- a compiler that implements K-optimization (`compileK`)
- a compiler that implements K- and Eta-optimization (`compileEta`)
- a compiler that generates code with *Bulk Combinators* (`compileBulk`)
- a compiler that eliminates *Bulk Combinators* with linear size(`compileBulkLinear`)
- a compiler that eliminates *Bulk Combinators* with logarithmic size(`compileBulkLog`)

## The Plain compiler
``` haskell
compilePlain :: Environment -> CL
compilePlain env = case lookup "main" env of
Nothing -> error "main function missing"
Just main -> snd $ plain env (deBruijn main)
I'll don't want to go into all the details of the algorithms. [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 outputs of the different compilers. And then I'll focus on performance comparisons between the different approaches.
I will use [my original compiler](https://github.com/thma/lambda-ski/blob/main/src/LambdaToSKI.hs) based on the classic (recursively optimized) bracket abstraction as a baseline for the performance comparisons.

### The simple `main` example

```haskell
main = λx y. * x y
```

| Compiler | Output |
| --- | --- |
| `benchmark: bracket abstraction` | `MUL` |
| `compilePlain` | `R I(B S(B(B MUL)(B K I)))` |
| `compileK` | `R I(B B(B MUL I)))` |
| `compileEta` | `MUL` |
| `compileBulk` | `MUL` |
| `compileBulkLinear` | `MUL` |
| `compileBulkLog` | `MUL` |

From this simple example it's obvious that `compilePlain` and `compileK` generate a lot of redundant code. All the other compilers generate the same output as the baseline.

Please also note that the Kiselyov algorithms may emit code for an additional `R` combinator with the following reduction rule:

```haskell
R f g x = g x f
```

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:
### The factorial function

```haskell
data CL = Com Combinator | INT Integer | CL :@ CL
fact = y(λf n. if (is0 n) 1 (* n (f (sub1 n))))
main = fact 100

data Combinator = I | K | S | B | C | Y | R | B' | C' | S' | T |
ADD | SUB | MUL | DIV | REM | SUB1 | EQL | GEQ | ZEROP
deriving (Eq, Show)
-- in de Bruijn Notation
("fact", A (Free "y") (L (L (A (A (A (Free "if") (A (Free "is0") (N Zero))) (IN 1)) (A (A (Free "*") (N Zero)) (A (N (Succ Zero)) (A (Free "sub1") (N Zero))))))))
("main", A (Free "fact") (IN 100))
```

The `plain` function is defined as follows:
| Compiler | Output |
| --- | --- |
| `benchmark: bracket abstraction` | `Y(B' S(C' IF ZEROP 1)(B' S MUL(C' S K SUB1))) 100` |
| `compilePlain` | `Y(B(S(R 1(B IF(B ZEROP I))))(B(S(B MUL I))(R(B SUB1 I)(B S(B K I))))) 100` |
| `compileK` | `Y(B(S(C(B IF(B ZEROP I)) 1))(B(S(B MUL I))(R(B SUB1 I)(B B I)))) 100` |
| `compileEta` | `Y(B(S(C(B IF ZEROP) 1))(B(S MUL)(R SUB1 B))) 100` |
| `compileBulk` | `Y(B(S(C(B IF ZEROP) 1))(B(S MUL)(C C SUB1 B))) 100` |
| `compileBulkLinear` | `Y(B(S(C(B IF ZEROP) 1))(B(S MUL)(C C SUB1 B))) 100` |
| `compileBulkLog` | `Y(B(S(C(B IF ZEROP) 1))(B(S MUL)(C C SUB1 B))) 100` |


What's interesting here is that only `compileEta` produces code of the same size as the baseline. All others produce code that uses at least one more combinator. Again `compilePlain` and `compileK` generate the largest code sizes.

### The fibonacci function

```haskell
fib = y(λf n. if (is0 n) 1 (if (eql n 1) 1 (+ (f (sub1 n)) (f (sub n 2)))))
main = fib 10

-- in de Bruijn notation
("fib", A (Free "y") (L (L (A (A (A (Free "if") (A (Free "is0") (N Zero))) (IN 1)) (A (A (A (Free "if") (A (A (Free "eql") (N Zero)) (IN 1))) (IN 1)) (A (A (Free "+") (A (N (Succ Zero)) (A (Free "sub1") (N Zero)))) (A (N (Succ Zero)) (A (A (Free "sub") (N Zero)) (IN 2)))))))))
("main", A (Free "fib") (IN 10))
```

| Compiler | Output |
| --- | --- |
| `benchmark: bracket abstraction` | `Y(B' S(C' IF ZEROP 1)(B' S(C' IF(C EQL 1) 1)(S' S(B' S(K ADD)(C' S K SUB1))(C' S K(C SUB 2))))) 10` |
| `compilePlain` | `Y(B(S(R 1(B IF(B ZEROP I))))(B(S(R 1(B IF(R 1(B EQL I)))))(S(B S(B(B ADD)(R(B SUB1 I)(B S(B K I)))))(R(R 2(B SUB I))(B S(B K I)))))) 10` |
| `compileK` | `Y(B(S(C(B IF(B ZEROP I)) 1))(B(S(C(B IF(C(B EQL I) 1)) 1))(S(B S(B(B ADD)(R(B SUB1 I)(B B I))))(R(C(B SUB I) 2)(B B I))))) 10` |
| `compileEta` | `Y(B(S(C(B IF ZEROP) 1))(B(S(C(B IF(C EQL 1)) 1))(S(B S(B(B ADD)(R SUB1 B)))(R(C SUB 2) B)))) 10` |
| `compileBulk` | `Y(B(S(C(B IF ZEROP) 1))(B(S(C(B IF(C EQL 1)) 1))(S2(B2 ADD(C C SUB1 B))(C C(C SUB 2) B)))) 10` |
| `compileBulkLinear` | `Y(B(S(C(B IF ZEROP) 1))(B(S(C(B IF(C EQL 1)) 1))(B(B S) B S(B B B ADD(C C SUB1 B))(C C(C SUB 2) B)))) 10` |
| `compileBulkLog` | `Y(B(S(C(B IF ZEROP) 1))(B(S(C(B IF(C EQL 1)) 1))(S B I(B(B S) B) I(S B I B ADD(C C SUB1 B))(C C(C SUB 2) B)))) 10` |


Here we see that `compileEta` produce code of the same size as the baseline. `compileBulk` generates code with one less combinator.

Please also note that `compileBulk` now emits code for additional bulk combinators `S2` and `B2`. I'll come back to the semantics of these later.


### The ackermann function

```haskell
plain :: Environment -> DB -> (Int, CL)
plain = convert (#) where
(0 , d1) # (0 , d2) = d1 :@ d2
(0 , d1) # (n , d2) = (0, Com B :@ d1) # (n - 1, d2)
(n , d1) # (0 , d2) = (0, Com R :@ d2) # (n - 1, d1)
(n1, d1) # (n2, d2) = (n1 - 1, (0, Com S) # (n1 - 1, d1)) # (n2 - 1, d2)

convert :: ((Int, CL) -> (Int, CL) -> CL) -> [(String, Expr)] -> DB -> (Int, CL)
convert (#) env = \case
N Zero -> (1, Com I)
N (Succ e) -> (n + 1, (0, Com K) # t) where t@(n, _) = rec $ N e
L e -> case rec e of
(0, d) -> (0, Com K :@ d)
(n, d) -> (n - 1, d)
A e1 e2 -> (max n1 n2, t1 # t2) where
t1@(n1, _) = rec e1
t2@(n2, _) = rec e2
IN i -> (0, INT i)
Free s -> convertVar (#) env s
where rec = convert (#) env

-- | convert a free variable to a combinator.
-- first we try to find a definition in the environment.
-- if that fails, we assume it is a combinator.
convertVar :: ((Int, CL) -> (Int, CL) -> CL) -> [(String, Expr)] -> String -> (Int, CL)
convertVar (#) env s
| Just t <- lookup s env = convert (#) env (deBruijn t)
| otherwise = (0, Com (fromString s))
ack = y(λf n m. if (is0 n) (+ m 1) (if (is0 m) (f (sub1 n) 1) (f (sub1 n) (f n (sub1 m)))))
main = ack 2 2

-- in de Bruijn notation
("ack", A (Free "y") (L (L (L (A (A (A (Free "if") (A (Free "is0") (N (Succ Zero)))) (A (A (Free "+") (N Zero)) (IN 1))) (A (A (A (Free "if") (A (Free "is0") (N Zero))) (A (A (N (Succ (Succ Zero))) (A (Free "sub1") (N (Succ Zero)))) (IN 1))) (A (A (N (Succ (Succ Zero))) (A (Free "sub1") (N (Succ Zero)))) (A (A (N (Succ (Succ Zero))) (N (Succ Zero))) (A (Free "sub1") (N Zero))))))))))
("main", A (A (Free "ack") (IN 2)) (IN 2))
```

| Compiler | Output |
| --- | --- |
| `benchmark: bracket abstraction` | `Y(B' S(B S(C'(B S K)(B IF ZEROP)(C ADD 1)))(S'(B S(S(K S)))(B' S(K(S(B IF ZEROP)))(B' S(K K)(C' S(C' S K SUB1)(K 1))))(S'(B S(S(K(B S K))))(C' S K SUB1)(C' S(S(K(B S K)))(K SUB1))))) 2 2` |
| `compilePlain` | `Y(B(S(B S(R(R 1(B ADD I))(B S(B(B IF)(B(B ZEROP)(B K I)))))))(S(B S(B(B S)(B(B(S(B IF(B ZEROP I))))(B(B(R 1))(R(B(B SUB1)(B K I))(B S(B(B S)(B(B K)(B K I)))))))))(S(B S(B(B S)(R(B(B SUB1)(B K I))(B S(B(B S)(B(B K)(B K I)))))))(B(R(B SUB1 I))(B(B S)(R(B K I)(B S(B(B S)(B(B K)(B K I)))))))))) 2 2` |
| `compileK` | `Y(B(S(B S(R(C(B ADD I) 1)(B B(B IF(B ZEROP I))))))(S(B S(B(B S)(B(B(C(B IF(B ZEROP I))))(B(R 1)(R(B SUB1 I)(B B I))))))(S(B S(B(B B)(R(B SUB1 I)(B B I))))(B(R(B SUB1 I))(B(B B)(R I(B B I))))))) 2 2` |
| `compileEta` | `Y(B(S(B S(R(C ADD 1)(B B(B IF ZEROP)))))(S(B S(B(B S)(B(B(C(B IF ZEROP)))(B(R 1)(R SUB1 B)))))(S(B S(B(B B)(R SUB1 B)))(B(R SUB1)(B B))))) 2 2` |
| `compileBulk` | `Y(B(S2(C C(C ADD 1)(B B(B IF ZEROP))))(S3(B2(C(B IF ZEROP))(C C2 1(C C SUB1 B)))(S2(B2 B(C C SUB1 B))(C C2 SUB1(B B))))) 2 2` |
| `compileBulkLinear` | `Y(B(B(B S) B S(C C(C ADD 1)(B B(B IF ZEROP))))(B(B S) B(B(B S) B S)(B B B(C(B IF ZEROP))(C(B(B C) B C) 1(C C SUB1 B)))(B(B S) B S(B B B B(C C SUB1 B))(C(B(B C) B C) SUB1(B B))))) 2 2` |
| `compileBulkLog` | `Y(B(S B I(B(B S) B) I(C C(C ADD 1)(B B(B IF ZEROP))))(B(B(B(B S) B))(S B I)(B(B S) B) I(S B I B(C(B IF ZEROP))(C(S B I(B(B C) B) I) 1(C C SUB1 B)))(S B I(B(B S) B) I(S B I B B(C C SUB1 B))(C(S B I(B(B C) B) I) SUB1(B B))))) 2 2` |

As mentioned in my last post the output size of braxcket abstraction grows quadratic with the number of variables.
In this case with three variables the output size for the bracket abstraction is already significantly larger than for
the previous example with two variables.

Now the Kiselyov algorithms really start to shine. `compileEta` produces code is significantly smaller as the baseline. And `compileBulk` output is even smaller.


### The tak function

```haskell
The main expression compiled to SICKBY combinator expressions by recursice bracket abstraction:
MUL
tak = y(λf x y z. (if (geq y x) z (f (f (sub1 x) y z) (f (sub1 y) z x) (f (sub1 z) x y ))))
main = tak 7 4 2

applying plain Kiselyov compilation:
R I(B S(B(B MUL)(B K I)))
-- in de Bruijn notation
("tak",A (Free "y") (L (L (L (L (A (A (A (Free "if") (A (A (Free "geq") (N (Succ Zero))) (N (Succ (Succ Zero))))) (N Zero)) (A (A (A (N (Succ (Succ (Succ Zero)))) (A (A (A (N (Succ (Succ (Succ Zero)))) (A (Free "sub1") (N (Succ (Succ Zero))))) (N (Succ Zero))) (N Zero))) (A (A (A (N (Succ (Succ (Succ Zero)))) (A (Free "sub1") (N (Succ Zero)))) (N Zero)) (N (Succ (Succ Zero))))) (A (A (A (N (Succ (Succ (Succ Zero)))) (A (Free "sub1") (N Zero))) (N (Succ (Succ Zero)))) (N (Succ Zero))))))))))
("main",A (A (A (Free "tak") (IN 7)) (IN 4)) (IN 2))
```

## to be continued...
| Compiler | Output |
| --- | --- |
| `benchmark: bracket abstraction` | `Y(B' S(B'(S(K S))(S(K S))(B' S(K IF)(B' S GEQ K)))(S'(B S(S(K(B S(S(K S))))))(S'(B S(S(K(B S(S(K S))))))(S'(B'(S(K(B'(S(K S)) K S))) K S) K(C' S K SUB1))(C'(B'(S(K(B S K))) S(S(K S)))(C' S K SUB1)(B K K)))(C'(B S(S(K(B'(S(K S)) K S))))(C'(B'(S(K S)) K S)(C' S K SUB1) K)(K K)))) 7 4 2` |
| `compilePlain` | `Y(B(S(B S(B(B S)(B(R I)(B(B S)(B(B(B IF))(B(S(B S(B(B GEQ)(B K I))))(B(B K)(B K I)))))))))(S(B S(B(B S)(B(B(B S))(S(B S(B(B S)(B(B(B S))(S(B S(B(B S)(B(B(B S))(B(B(B K))(B(B K)(B K I))))))(B(B(R I))(B(B(B S))(B(R(B K I))(B(B S)(B(B(B S))(R(B(B(B SUB1))(B(B K)(B K I)))(B S(B(B S)(B(B(B S))(B(B(B K))(B(B K)(B K I))))))))))))))))(R(B(B K)(B K I))(B S(B(B S)(B(B(B S))(B(B(R I))(B(B(B S))(B(R(B(B SUB1)(B K I)))(B(B S)(B(B(B S))(B(B(B K))(B(B K)(B K I))))))))))))))))(B(R(B K I))(B(B S)(B(B(B S))(R(B(B K)(B K I))(B S(B(B S)(B(B(B S))(B(B(R(B SUB1 I)))(B(B(B S))(B(B(B K))(B(B K)(B K I)))))))))))))) 7 4 2` |
| `compileK` | `Y(B(S(B S(B(B S)(B(R I)(B(B B)(B(B IF)(B(C(B GEQ I)) I)))))))(S(B S(B(B S)(B(B(B S))(S(B S(B(B S)(B(B(B S))(S(B B(B B(B B I)))(B(B(R I))(B(B(B B))(B(R I)(B(B B)(R(B SUB1 I)(B B I))))))))))(R I(B B(B C(B(B C)(B(R I)(B(B B)(R(B SUB1 I)(B B I))))))))))))(B(R I)(B(B B)(B(B C)(R I(B B(B C(R(B SUB1 I)(B B I)))))))))) 7 4 2` |
| `compileEta` | `Y(B(S(B S(B(B S)(B(B IF)(C GEQ)))))(S(B S(B(B S)(B(B(B S))(S(B S(B(B S)(B(B(B S))(S(B B(B B B))(R SUB1 B)))))(B C(B(B C)(R SUB1 B)))))))(B(B C)(B C(R SUB1 B))))) 7 4 2` |
| `compileBulk` | `Y(B(S3(B2 IF(C GEQ)))(S4(S4(S B3(C C SUB1 B))(B C2(C C SUB1 B)))(B2 C(B C(C C SUB1 B))))) 7 4 2` |
| `compileBulkLinear` | `Y(B(B(B S) B(B(B S) B S)(B B B IF(C GEQ)))(B(B S) B(B(B S) B(B(B S) B S))(B(B S) B(B(B S) B(B(B S) B S))(S(B B(B B B))(C C SUB1 B))(B(B(B C) B C)(C C SUB1 B)))(B B B C(B C(C C SUB1 B))))) 7 4 2` |
| `compileBulkLog` | `Y(B(B(B(B(B S) B))(S B I)(B(B S) B) I(S B I B IF(C GEQ)))(S B I(S B I(B(B S) B)) I(S B I(S B I(B(B S) B)) I(S(B(B B)(S B I) B)(C C SUB1 B))(B(S B I(B(B C) B) I)(C C SUB1 B)))(S B I B C(B C(C C SUB1 B))))) 7 4 2` |

In this example with four variables the trend continues. `compileEta` produces code is significantly smaller as the baseline. And `compileBulk` output now is only about 1/3 of the baseline.


## performance comparison

So far we have seen that for functions with more than two variables the Kiselyov algorithms generate code that is significantly smaller than optimized versions of classic bracket abstraction.
But what about performance? Is the code generated by the Kiselyov algorithms also faster?

To answer this question i have implemented a simple benchmarking suite based on the [micro-benchmarking framework Criterion](http://www.serpentine.com/criterion/).

In my suite I am testing the performance of combinations of the following components:

- the compilers `compileBracket`, `compileEta` and `compileBulk` from the previous section
- the function factorial, fibonacci, ackermann and tak from the previous section
- the execution backenda Graph Reduction Engine and the native Haskell functions implementaion from my previous post


##


![Alt text](image.png)

## Conclusion
8 changes: 7 additions & 1 deletion src/Kiselyov.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ module Kiselyov
deBruijn,
bulkOpt,
compilePlain,
compileBulk,
compileK,
compileEta,
compileBulk,
compileBulkLinear,
compileBulkLog,
optK,
Expand Down Expand Up @@ -75,6 +76,11 @@ bulk :: Combinator -> Int -> CL
bulk c 1 = Com c
bulk c n = Com $ BulkCom (show c) n

compileK :: Environment -> CL
compileK env = case lookup "main" env of
Nothing -> error "main function missing"
Just main -> snd $ optK env (deBruijn main)

compileEta :: Environment -> CL
compileEta env = case lookup "main" env of
Nothing -> error "main function missing"
Expand Down
4 changes: 4 additions & 0 deletions src/LambdaToSKI.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module LambdaToSKI
( compileEither,
compile,
compileBracket,
abstractToSKI,
abstractSimple,
abstractToCCC,
Expand Down Expand Up @@ -98,6 +99,9 @@ compileEither env abstractFun = case lookup "main" env of
Nothing -> Left $ "main function missing in " ++ show env
Just main -> Right $ abstractFun env main

compileBracket :: Environment -> CL
compileBracket env = compile env abstractToSKI

compile :: Environment -> (Environment -> Expr -> Expr) -> CL
compile env abstractFun =
case compileEither env abstractFun of
Expand Down

0 comments on commit d64d84a

Please sign in to comment.