Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jemtaly/projectlambda

A calculator / programming language based on lambda calculus. 基于 Lambda 演算的简易编程语言。
https://github.com/jemtaly/projectlambda

calculator esolang functional-programming lambda lambda-calculus programming-language

Last synced: about 2 months ago
JSON representation

A calculator / programming language based on lambda calculus. 基于 Lambda 演算的简易编程语言。

Awesome Lists containing this project

README

        

# ProjectLambda

ProjectLambda is a calculator / programming language based on lambda calculus.

## Compiling

```
git clone https://github.com/Jemtaly/ProjectLambda
cd ProjectLambda
make SOURCE=lambda_cbv.cpp # Global variable Call-by-value (hereinafter referred to as CBV) version
make SOURCE=lambda_cbv.cpp USE_GMP=1 # CBV version using GNU MP Bignum Library
make SOURCE=lambda_cbn.cpp # Global variable Call-by-need (hereinafter referred to as CBN) version (Experimental, see note below)
make SOURCE=lambda_cbn.cpp USE_GMP=1 # CBN version using GNU MP Bignum Library
```

## Commands

| instruction | usage |
| --- | --- |
| `cal FORMULA` | Calculate the formula. |
| `:VAR FORMULA` | Define a global variable/function. |
| `dir` | List all the defined functions/variables. |
| `clr` | Clear the defined functions/variables. |
| `end` or eof | Exit the program. |

## Syntax

The basic syntax of ProjectLambda is the same as the standard form of the lambda algorithm. Applications are assumed to be left associative: `M N P` and `((M N) P)` are equivalent. Other syntax is as follows:

| symbol | meaning |
| --- | --- |
| `\PAR EXPR` | Lambda expression, `PAR` is the formal parameter, `EXPR` is the body, the body of an abstraction extends as far right as possible. Example: `\x \y $y $x` => $\lambda x.\lambda y.y\ x$ |
| `EXPR \|PAR1 ARG1 \|PAR2 ARG2 ...` | This is a syntactic sugar for lambda expressions whose arguments are already determined. If you know haskell, think of it as something like `where` in that.
`\|PAR` has lower priority than apply, but higher than `\PAR`. Example: `\y $x $y $z \|z + 1 2 \|x \a \b $t $a $b \|t +` => `\y ($x $y $z \|z (+ 1 2) \|x \a \b ($t $a $b \|t +))`. |
| `^PAR EXPR` | This is similar to `\PAR EXPR`, the difference is, the argument to `\PAR EXPR` is lazy-evaluated, while the argument to `^PAR EXPR` is eager-ealuated, which means its argument will be computed before they are brought into the function. |
| `EXPR @PAR1 ARG1 @PAR2 ARG2 ...` | Syntactic sugar for eager-evaluated lambda expression. |
| `$ARG` | Formal parameter, used in lambda expressions. |
| `&VAR` | Call the global function/variable defined by `:VAR` instruction. (Only for CBN version, use `$VAR` instead in CBV version.) |
| `+` `-` `*` `/` `%` | Binary operators, in the form of operands-swapped prefix expressions, e.g., $(5-2)/3$ should be represented as `/ 3 (- 2 5)`. |
| `>` `<` `=` | Comparison operators, which takes four arguments, compares the first two arguments (as same as binary operaters, the operands should be swapped) and returns the third if the result is true and the fourth if the result is false. For example, `> 1 2 3 4` equals to `2 > 1 ? 3 : 4` in C. |
| `...` | Nil. As a function, it gets itself when applied to any argument. |

**Note:** *Spaces in expressions cannot be omitted.*

## Syntax details

*Unless otherwise specified, the examples below are for the CBV version.*

### First example

```
cal (\a \o \b $o $b $a) 10 - 3
# output: 7
```

**Computational procedure:** `(\a \o \b $o $b $a) 10 - 3` => `(\o \b $o $b 10)` => `(\b - $b 10) 3` => `- 3 10` => `7`

### Lazy evaluation

```
cal (\x \y $y) ((\f $f $f) \f $f $f) 0
# output: 0
```

**Computational procedure:** `(\x \y $y) ((\f $f $f) \f $f $f) ((\a \o \b $o $b $a) 12 / 4)` => `(\y $y) 0` => `0`

**Explain:** The calculation of `(\f $f $f) \f $f $f` will cause the program to run indefinitely. However, since `\PAR EXPR` is lazy-evaluated, in `\x \y $y`, the formal parameter `$x` corresponding to `(\f $f $f) \f $f $f` is not used, so it is not actually calculated, thus we successfully get the expected result.

### Scope of formal parameters

```
cal (\x \x $x) 1 2
# output: 2
```

**Computational procedure:** `(\x \x $x) 1 2` => `(\x $x) 2` => `2`

**Explain:** When there are multiple parameters with the same name, the internal formal parameter will match the nearest actual parameter.

### Use of `|PAR`

This is a syntactic sugar for lambda expressions whose arguments are already determined. If you know haskell, think of it as something like `where` in that.

Examples:

- `(* (f x y z) (f x y z))` => `* $t $t |t f x y z`
- `(* (f x y z) (g x y z))` => `* $s $t |s f x y z |t g x y z`
- `(* (f (x y)) (g (x y)))` => `* $s $t |s f $u |t g $u |u x y`

### Difference between `\PAR EXPR` and `^PAR EXPR`

As mentioned earlier, the parameter of `\PAR EXPR` is lazy-evaluated, while the parameter of `^PAR EXPR` is eager-evaluated. Usually, we only need to use the former (because it avoids calculating parameters that do not need to be used). However, since in ProjectLambda, if the function is not applied, its internal content will not be calculated, so if you want to construct a tuple (`\o ... ... ...`) and output, then, with `\PAR EXPR` syntax, you will get a long list of raw results:

```
:LazyTriple \a \b \c \o $o $a $b $c
cal &LazyTriple (+ 0 0) (+ 1 1) (+ 2 2)
# output: \o $o (+ 0 0) (+ 1 1) (+ 2 2)
```

Obviously, this is not what we want, and if you use the syntax of `^PAR EXPR`, the parameters will be calculated first and then substituted into the function:

```
:EagerTriple ^a ^b ^c \o $o $a $b $c
cal &EagerTriple (+ 0 0) (+ 1 1) (+ 2 2)
# output: \o $o 0 2 4
```

In fact, using this syntax, if you have a lazy-evaluated infinite array (of the form `\o $o ... \o $o ... \o $o \o $o ... ...`), you can easily calculate and output the value of its first n elements through the following function:

```
:LazyCons \a \b \o $o $a $b
:car \list $list \a \b $a
:cdr \list $list \a \b $b
:EagerCons ^a ^b \o $o $a $b
:eval \n \lazylist = 0 $n ... (&EagerCons (&car $lazylist) (&eval (- 1 $n) (&cdr $lazylist)))
```

*Note: `...` is used here to denote an empty list.*

Example:

```
:fib \m \n &LazyCons $m (&fib $n (+ $m $n))
cal &fib 0 1
# output: \o $o 0 (&fib 1 (+ 0 1))
cal &eval 10 (&fib 0 1)
# output: \o $o 0 \o $o 1 \o $o 1 \o $o 2 \o $o 3 \o $o 5 \o $o 8 \o $o 13 \o $o 21 \o $o 34 ...
```

### Difference between CBN and CBV Version

The main difference between the CBN and CBV versions lies in the storage and calling of global variables: in the CBN (Global variable Call-by-need) version, global variables are stored in shared pointers, just like arguments in lambda calculus, so they are only evaluated once if needed. However, to avoid circular references, named functions in this version cannot directly recursively call themselves or undefined functions, they must use Y combinators to implement recursion, just like those anonymous functions. On the other hand, in the CBV (Global variable Call-by-value) version, global functions and variables are stored directly as unevaluated syntax trees, and a new copy is created and recalculated each time they are used. Therefore, named functions in this version can directly reference themselves or variables/functions that are not yet defined.

### Calculating the factorial of 99 using recursion

- Method A (Only available for CBV version)

```
:fact \n > 0 $n (* $n (&fact (- 1 $n))) 1
cal &fact 99
# output: 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000
```

- Method B (Fixed-point combinator)

```
:Y \g (\x $g ($x $x)) \x $g ($x $x)
:G \f \n > 0 $n (* $n ($f (- 1 $n))) 1
:fact &Y &G
cal &fact 99
# output: 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000
```