Warning
This language is in developement and is unfinished
- What is Lamb?
- Usage
- What is Lambda Calculus?
- Call-by-Value (CBV)
- Language Reference
- StdLamb - Standard Library
Lamb is a lambda calculus based programming language without all the bells and whistles, implemeted purely in raw lambda calculus. The parser and interpreter archicture is written in C.
- Use any compiler you wish to compile
build/richBuild.c, for instance:gcc build/richBuild.c -o richBuild
- run the
richBuildexecutable
On a unix Operating system run the richBuild execuable to generate a Lamb executable.
./Lamb
- Launches the REPL
./Lamb -i inputfile.l
- Interprets a passed in file
Edit build/richBuild.c to add debugging flags to cflags
-DLOGGING: logs reduction steps during Computation-LOGTREES: log parse trees for expressions during excution.
Some examples, are located in examples/ these are currently sprase but should grow when I get some more time.
Lambda Calculus is one of the simplest models for computation designed by Alonzo Church (1936).
In this model we consider a function
Multiple arguments are support through function application, or Currying, therefore the Lambda calculus equivalent of
The Grammar of Lamb follows closely with the lambda claculus Grammar.
<assignment> ::= <variable> `:=` <expression>
<expression> ::= <name> | <function> | <application>
<function> ::= (`λ` <name>`.`<expression>)
<application> ::= (<expression> <expression>)
<variable> ::= <name> | <variable> <name>
<name> ::= [Aa-Zz]
Lambda Calculus has 2 main rules of computation, these are known as Beta Reduction and Alpha conversion.
Key Terms:
-
$[N/x]$ means we are substituting all x for N. - Each rule is a syllogism:
- Consisting of a premise above the line.
- And a conclusion below the line.
- The line means therefore.
Beta Reduction
- We can run this syllogism under the condition
$free(N) \cap bound(M) = \emptyset$ , meaning that there are no free varaibles N, that are bound in M. - In Beta Reduction we substitute all x's for N, for example in
$(\lambda x . x + 1)$ we discard$\lambda x$ swap the$x$ for$N$ resulting in$(N + 1)$ . - Note that
$A$ and$B$ remain unchanged.
Alpha Conversion
- Alpha conversion is used to avoid variable name clashes.
- We can swap
$y$ with$x$ as long as$x$ is not already in the expression and the overall meaning is retained.
Eta Reduction
- Eta Reduction is an optimisation, which simplifies expressions which do nothing but apply another function.
- The expression
$\lambda x . (M x)$ can be reduced to just$M$ if$x$ does not appear freely in$M$ . - This means the function doesn't actually use
$x$ in any meaningful way — it's just passing it along — so the wrapper is unnecessary.
- What it is: arguments are evaluated before function application, and in Lamb function bodies inside abstractions are also reduced eagerly.
- Effect: an application
(F X)first reducesFandXto values, then substitutes.
- Effect: an application
- How it applies to Lamb: classic
Y-combinator or naive conditionals can diverge under CBV.- Use CBV-safe patterns: a CBV fixpoint like
Z, and thunked branches forIF(pass\_ . ...then force withID).
- Use CBV-safe patterns: a CBV fixpoint like
- Writing Lamb code under CBV: define non-strict constructs explicitly.
- Prefer CBV-friendly encodings (e.g.,
IS_ZERO := (\n . n (\_ . F) T),MUL := (\m n f x . m (n f) x)).
- Prefer CBV-friendly encodings (e.g.,
- Alternative (Call-by-Name / Need): substitutes arguments without evaluating them first.
- Pros: classic
Yand unthunkedIFwork; Cons: may duplicate work (CBN); call-by-need adds sharing but changes evaluator design.
- Pros: classic
We can define functions using the \ Syntax for Lambda.
(\x . x)
This is the identity function, it takes a variable x and returns a variable x.
Functions can be applied in a similar fashion:
(\x . x)(\y . y)
There we apply the function (\y . y) to the identity function resulting in (\y . y)
This works because the function (\y . y) gets substituted in place of the bound varaible x (\x) gets dropped and we end with (\y . y). A worked example can be found here.
We can create expressions of multiple arguments using Currying.
(\x y . x y)
Here is the Lamb equivalent of
Similarly brackets are not needed for application, expressions are evaluated greedily from the left, the following two statements are equivalent:
(AND TRUE FALSE)
((AND TRUE) FALSE)
This is also known as reverse polish notation which can be found in stack based languages such as Forth.
A key part of witing programs is being able to reuse expressions. To do this in Lamb we use the := operator to bind an expression to a name.
True := (\t f . t)
False := (\t f . f)
And := (\a b . a b F)
(AND TRUE FALSE) -- FALSE
Comments are defined using the Haskell Style -- synax.
-- This is a Comment
(\x . x x a)(\a b . b) -- this is an expression
Definitions can be reused via modules. Any .l file which contains only definition statements (VAR := expr) and comments can be treated as a module.
#import "module.l"
(VAR)(\x . x)
All definitions from the module become available in the file.
Warning
Realtive imports resolve to "examples/" so place it all in the examples. Or edit the source code. Completely Relative imports are not currently supported, so the full path can be provided.
Import once at the top of your file:
#import "stdLamb.l"
- T, F: Church booleans.
- T being True and F being False
Example
(T a b) -> a (F a b) -> b
- returns
aifais true, otherwiseF.Example
(AND T F) -> F
- returns
Tifais true, elseb.Example
(OR F T) -> T
- boolean negation.
Example
(NOT T) -> F
- selects between branches using a Church boolean
b.- CBV-safe usage: pass thunks and force with
ID.
Example (CBV)
(IF (IS_ZERO ZERO) (\_ . ONE) (\_ . TWO) ID) -> ONE - CBV-safe usage: pass thunks and force with
Tif Church numeralnis zero; elseF.Example
(IS_ZERO ZERO) -> T (IS_ZERO ONE) -> F
- Church numerals 0..10.
Example
(ZERO f x) -> x (ONE f x) -> (f x)
- The successor function
Example
(S ZERO) -> ONE
- addition.
Example
(PLUS TWO THREE) -> FIVE
- multiplication.
Example
(MUL TWO THREE) -> SIX
- predecessor (clamped at ZERO).
Example
(PRED THREE) -> TWO
- GTE x y:
Tifx ≥ y, elseF.Example
(GTE THREE TWO) -> T - EQ x y:
Tifx = y, elseF.Example
(EQ TWO TWO) -> T
- Church pair; apply to a consumer to choose.
- First/second projection.
Example
(FST (PAIR A B)) -> A (SND (PAIR A B)) -> B
- classic fixpoint combinator (works in call-by-name). Under CBV it may diverge; prefer CBV-safe patterns (thunks or a CBV fixpoint) when needed.
Example (conceptual)
(Y FACTF) ONE -> factorial(1)
Note
It is suggested to use an interative approach for most things for now. See fatorial.
- Identity function
Example
(ID X) -> X
- self-application helper
(\x . x x); apply with a binary function-style argument.Example
(IDENTITY (\y . y)) -> ((\y . y) (\y . y))
