Better readme https://hackmd.io/_L-hJmJRRnauuN_ENEFeOA # Lambda Calculus Compiler ## Hussain Kara Fallah - Almir Mullanorov The goal was to build an interpreter for applied lambda calculus. ### BNF ``` <Var> = many <letter> <Single> = <Var> | <Number> | <Boolean> <Term> = <single> | ( <leftbrack> <Expression> <Rightbrack> ) <Arithmetic> = <Term> ( <Operation> <Arithmetic> | <Nothing> ) <applicable> = <Recursive> | <LambdaAbstraction> | <Arithmetic> <SuccessiveApp> = lfold (many <applicable>) <LambdaAbstracion> = <"\"> <Var> <":"> <Type> <"."> <Expression> <Expression> = <SuccessiveApp> <|> <IfElse> <IfElse> = <"if"> <Expression> <"?"> <Expression> <":"> <Expression> <RecursiveExpression> = <"rec"> <Expression> <Type> = <TypeAtom> ( ( <"->"> <TypeAtom> ) | <Nothing>) <TypeAtom> = <"Int"> | <"Bool"> | (<"("> <Type> <")">) <Let> = <"Let"> <Var> <"="> <SuccessiveApp> <";"> <Lang>= <Let> | <Expression> ``` ### AST ![](https://i.imgur.com/DphpZOc.png) ### Basic Syntax: Let operations are used to write global definitions for the whole program. ``` Let IDENTIFIER = LAMBDA_EXPRESSION ; ``` Identifier must be an UpperCase Word Must have a valid lambda expression on the right hand. Lambda expressions are wrote in the following way: ``` \x . x + 1 $ 10 ``` The dot separates the variable from the body The dollar declares the end of the body Other examples could be: ``` \x.\y.y$$ ((\x.x$) (\y.y$)) \x y . x + y $ (\z.z+1 $ 10) (2 + 9) ``` ## Samples: #### Sample1: $f(x,y,z)=10$ ``` \x y z . 10 $ 500 250 ``` This lamba expression is reduced 10 #### Sample2: A function that checks that at least 2 of 3 variables are true ``` \x y z . (x & y) | (x & z) | (y & z) $ True False False ``` #### Sample 3: f(x,y,z) = (x + 1) * (y + 2) * (z + 3) ``` \x y z . (\a.a+1 $ x) * (\a.a+2 $ y) * (\a.a+3 $ z) $ 0 0 0 ``` #### Sample4 $f(x,y,z) = Inc(x) * Dec(y) * (z + 2)$ $Inc(x) = x+1$ $Dec(x) = x-1$ $f(1,3,0) = 2*2*2=8$ ``` let INC = \x.x+1$; let DEC = \x.x-1$; let T = True; \x y z . (INC x) * (DEC y) * (z + 2) $ (0 + 1) 3 0 ``` #### Sample5 $(\lambda x. \lambda y . y) \, ( \, ( \lambda x.x ) \, ( \lambda y.y ) )$ This one is equivalent to: $\lambda y.y$ ``` \x.\y.y$$ ((\x.x$) (\y.y$)) ``` ### Sample 6: $(\lambda x \, y \, z . x \, y \, z) (\lambda x. x \, x) (\lambda y.y)$ This one is eqivalent to: $x$ ``` (\x y z . x y z $) (\x . x x $) (\x . x $) x ``` ## 2nd iteration ## Main evaluation rules $$\frac{}{(\lambda x.e)\, v \rightarrow e\,[v/x]}$$ $$\frac{e_1 \rightarrow e_1'}{e_1\,e_2 \rightarrow e_1'\,e_2}$$ $$\frac{e_2 \rightarrow e_2'}{v \,e_2 \rightarrow v\,e_2'}$$ $$\frac{}{((\lambda x.e_1) \oplus c) \, e_2 \rightarrow (\lambda x.e_1 \oplus c) \, e_2}$$ ## Type checking rules $$\frac{\Gamma,x:\tau_1 \vdash e : \tau_2}{\Gamma \vdash (\lambda x.e) : \tau_1 \rightarrow \tau_2}$$ $$\frac{\Gamma \vdash e_1:\tau_2 \rightarrow \tau_1 \qquad \Gamma\vdash e_2 : \tau_2}{\Gamma \vdash e_1 \, e_2: \tau_1}$$ $$\frac{\Gamma \vdash e_1:\tau_3 \rightarrow \tau_2 \qquad \Gamma\vdash e_2 : \tau_1 \rightarrow \tau_3}{\Gamma \vdash e_1 \, e_2: \tau_1 \rightarrow \tau_2}$$ ## Recursion The recursion is done using the fix-point combinator. I won't explain the whole theory I will just show how it magically works. For good understanding check this nice talk: [Ruby Conf 12 - Y Not- Adventures in Functional Programming by Jim Weirich](https://www.youtube.com/watch?v=FITJMJjASUs) $fix \;\lambda f . \lambda n. (body)$ Let's see how an infinite function looks like $fix \;\lambda f . \lambda n. n * f \>(n-1)$ This is equivalent to : $n*(n-1)*(n-2)*(n-3)...$ I have used such function for simplicity Now here's evaluation rules: $$\frac{e \rightarrow e'}{fix \; e \rightarrow fix \; e'}$$ $$\frac{}{fix \;\lambda f .e \rightarrow e\, [fix \;\lambda f .e / f]}$$ Let's try it: $(fix \;\lambda f . \lambda n. n * f \>(n-1)) \, (3)$ ~ $\lambda n. n * f \>(n-1) [fix \;\lambda f .e / f] \rightarrow$ $(\lambda n. n * (fix \;\lambda f . \lambda m. m * f \>(m-1))(m-1)) (3)$ -> $3 * (fix \;\lambda f . \lambda m. m * f \>(m-1)) (2)$ -> $3* (\lambda m. m * (fix \;\lambda f . \lambda k. k * f \>(k-1)) \>(m-1))(2)$ -> $3*2* (fix \;\lambda f . \lambda k. k * f \>(k-1)) \>(1)$ You should have seen the recursion by now) The compiler was upgraded and now it supports the following: 1. If/else and boolean clauses evaluation 2. Currying 3. Higher order functions 4. Static Type system (Curch & Curry) 5. Lambda recursion Now you would need to give a type to the parameter of each lambda and the compiler will check the type before accepting/evaluating the instruction. ## Samples ### Sample1 simple max function evaluation test max(3,5) must be evaluated to 5 ``` it "max evaluation test" $ runeval Map.empty "\\x : Int y : Int . if x > y ? x : y $ 3 5" `shouldBe` (Literal (XInt 5)) ``` ### Sample 2 equality check function applied to 2 integers 3 == 3 must evaluate to True ``` it "equality test" $ runeval Map.empty "\\x : Int y : Int . if x = y ? True : False $ 3 3" `shouldBe` (Literal (XBool True)) ``` ### Sample 3 Type checking for the previous equality function (not applied to anything though) our function must be: Int -> Int -> Bool ``` it "if with 2 normal clauses" $ checktest "\\x : Int . \\ y : Int . if x = y ? True : False $ $ " `shouldBe` (Right $ TArr TInt (TArr TInt TBool)) ``` ### Sample 4 Type check error when a doing and between 2 integers f(x : int ,y : int) = x & y should result in mismatch error expected bool received int ``` it "and beteween 2 integers should result in error" $ checktest "\\x : Int y : Int . x & y $ 4 5" `shouldBe` (Left $ TypeMismatch TInt TBool) ``` ### Sample 5 a lambda with if statement that may return 2 different types The function is equivalent to ``` f x | x == 5 = x + 1 | otherwise = True ``` ``` it "if with 2 different return clauses" $ checktest "\\x : Int . if x = 5 ? x + 1 : True $ 5" `shouldBe` (Left $ TypeMismatch TInt TBool) ``` ### Sample 6 a sum function applied to one argument yields a function that accepts one argument ``` f :: Int -> Int -> Int f x y = x + y :t (f 4) ``` ``` it "sum of 2 values applied to one is int->int" $ checktest "\\x : Int . \\y : Int . x + y $ $ 4" `shouldBe` (Right $ TArr TInt TInt) ``` ### Sample 7 Factorial recursion 5! = 120 ``` (rec \\f : Int . \\n : Int . if n=0 ? 1 : (if n = 1 ? 1 : n * (f (n-1))) $ $) 5 ``` ## Sample program 1: ``` f :: (Int -> Int) -> (Int -> Int) -> (Int -> Int) f xx yy = xx . yy g :: (Int -> Int) -> (Int -> Int) g = f (\x -> x + 1) h :: Int -> Int h = g (\x -> x * 2) a :: Int -> (Int -> Int) a x = (\y -> y + x) z :: Int -> Int z = a 5 ``` ``` let F = (\x : Int -> Int . \y : Int -> Int . x y $ $); let G = F (\x : Int . x + 1 $); let H = G (\x : Int . x * 2 $); let XX = G (\x : Int . True $); let HH = F (\x : Int . x + 1 $) (\x : Int . x * 2 $); H 5 let A = (\x : Int . \y : Int . x + y $ $); let Z = A 5; Z 1 Z 2 ``` ## Sample program 2: ``` f :: Int -> Int -> Int f n m = | m == 1 = n | otherwise = n * (f n (m - 1)) -- check type and do evals g :: Int -> Int g = f 2 -- check type and do evals ``` ``` let P = (rec \f : Int -> Int -> Int . \n : Int . \m : Int . if m=1 ? n : n * (f n (m-1)) $ $ $) ; let G = P 2; G 3 G 4 ```