45
22
The challenge is to write an interpreter for the untyped lambda calculus in as few characters as possible. We define the untyped lambda calculus as follows:
Syntax
There are the following three kinds of expressions:
A lambda expression has the form
(λ x. e)wherexcould be any legal variable name andeany legal expression. Herexis called the parameter andeis called the function body.For simplicity's sake we add the further restriction that there must not be a variable with the same name as
xcurrently in scope. A variable starts to be in scope when its name appears between(λand.and stops to be in scope at the corresponding).- Function application has the form
(f a)wherefandaare legal expressions. Herefis called the function andais called the argument. - A variable has the form
xwherexis a legal variable name.
Semantics
A function is applied by replacing each occurrence of the parameter in the functions body with its argument. More formally an expression of the form ((λ x. e) a), where x is a variable name and e and a are expressions, evaluates (or reduces) to the expression e' where e' is the result of replacing each occurrence of x in e with a.
A normal form is an expression which can not be evaluated further.
The Challenge
Your mission, should you choose to accept it, is to write an interpreter which takes as its input an expression of the untyped lambda calculus containing no free variables and produces as its output the expression's normal form (or an expression alpha-congruent to it). If the expression has no normal form or it is not a valid expression, the behaviour is undefined.
The solution with the smallest number of characters wins.
A couple of notes:
- Input may either be read from stdin or from a filename given as a command line argument (you only need to implement one or the other - not both). Output goes to stdout.
- Alternatively you may define a function which takes the input as a string and returns the output as a string.
- If non-ASCII characters are problematic for you, you may use the backslash (
\) character instead of λ. - We count the number of characters, not bytes, so even if your source file is encoded as unicode λ counts as one character.
- Legal variable names consist of one or more lower case letters, i.e. characters between a and z (no need to support alphanumeric names, upper case letters or non-latin letters - though doing so will not invalidate your solution, of course).
- As far as this challenge is concerned, no parentheses are optional. Each lambda expression and each function application will be surrounded by exactly one pair of parentheses. No variable name will be surrounded by parentheses.
- Syntactic sugar like writing
(λ x y. e)for(λ x. (λ y. e))does not need to be supported. - If a recursion depth of more than 100 is required to evaluate a function, the behaviour is undefined. That should be more than low enough to be implemented without optimization in all languages and still large enough to be able to execute most expressions.
- You may also assume that spacing will be as in the examples, i.e. no spaces at the beginning and end of the input or before a
λor.and exactly one space after a.and between a function and its argument and after aλ.
Sample Input and Output
Input:
((λ x. x) (λ y. (λ z. z)))Output:
(λ y. (λ z. z))Input:
(λ x. ((λ y. y) x))Output:
(λ x. x)Input:
((λ x. (λ y. x)) (λ a. a))Output:
(λ y. (λ a. a))Input:
(((λ x. (λ y. x)) (λ a. a)) (λ b. b))Output:
(λ a. a)Input:
((λ x. (λ y. y)) (λ a. a))Output:
(λ y. y)Input:
(((λ x. (λ y. y)) (λ a. a)) (λ b. b))Output:
(λ b. b)Input:
((λx. (x x)) (λx. (x x)))Output: anything (This is an example of an expression that has no normal form)
Input:
(((λ x. (λ y. x)) (λ a. a)) ((λx. (x x)) (λx. (x x))))Output:
(λ a. a)(This is an example of an expression which does not normalize if you evaluate the arguments before the function call, and sadly an example for which my attempted solution fails)Input:
((λ a. (λ b. (a (a (a b))))) (λ c. (λ d. (c (c d)))))Output:
`(λ a. (λ b. (a (a (a (a (a (a (a (a b))))))))))This computes 2^3 in Church numerals.
3Many or all of the solutions here fail to implement capture-avoiding substitution! You should add a test case like ((λ f. (λ x. (f x))) (λ y. (λ x. y))), which should evaluate to (λ x. (λ z. x)), not (λ x. (λ x. x)). – Anders Kaseorg – 10 years ago
@Kaseorg in ((λ f. (λ x. (f x))) (λ y. (λ x. y))) what is the result? is it (λ x. (λ z. x))? what is z? – RosLuP – 9 years ago
what is the result of "(λ a. ((( w(a) ))) )z" "w(z)" "(w(z))" or "((( w(z) )))" ? – RosLuP – 9 years ago
the lambda program allow the result of (/x.(/y. x^2+y^2)3)7 to be 7^2+3^2? – RosLuP – 9 years ago
@RosLuP The correct result is
(λ x. (λ z. x))because the function should return the outer function's argument, not the inner function's argument.zis a new identifier that was chosen overx, becausexwould shadow the outerx, leading to a wrong result. Instead ofzany other valid identifier could also have chosen.(λ a. ((( w(a) ))) )zis not an input you need to handle as it doesn't adhere to the parenthesization rules I've described (i.e. one pair of parentheses around ever abstraction and application and that's it) and because there won't be free variables in the input. – sepp2k – 9 years ago@RosLuP So the result can be anything you want, but I'd say the most correct result would still be conforming to the parenthesization rules, so
(w z). There's no need to support numbers or infix operators. – sepp2k – 9 years ago(w z) has parentesis different from (λ x. (λ z. x)) so it would be [λ x. [λ z. x]] – RosLuP – 9 years ago
@RosLuP What do you mean? As I said, the rules are to parenthesize function application and abstraction (with one pair of parentheses each) and nothing else. Both
(w z)and(λ x. (λ z. x))follow these rules exactly. I'm not sure what you mean by "so it would be [λ x. [λ z. x]]". Where did the square brackets come from all of the sudden? – sepp2k – 9 years agoi'm not one expert and this is the first time i see lambda calculus... now i see (a w) as one function and (λ z. x) as a macro substitution to strings... so they are different and for not confuse parentesis different they for me have to have different type of parentesis so [λ z. x] – RosLuP – 9 years ago
@RosLuP First of all
(λ z. x)creates a function (or is a function, really) and(f x)applies a function. So both deal with functions - there are no macros in the lambda calculus. Secondly addition is different from multiplication, but nobody would suggest that something like((x + y) * z)should instead be written([x + y] * z). That is, there's no rule that says that different types of expressions need to be parenthesized with different types of parentheses. No programming language in the world has such a rule (inb4 obscure counter example). Thirdly the usual syntax ... – sepp2k – 9 years ago... for the lambda calculus uses round parentheses everywhere and that's what this challenge asks for. – sepp2k – 9 years ago
1@sepp2k Have you considered adding ((λ f. (λ x. (f x))) (λ y. (λ x. y))) as a test case and unaccepting the current answer which incorrectly produces (λ x. (λ x. x))? – Anders Kaseorg – 9 years ago
@RosLup. The presence of a lambda indicates whether an expression is a macro or a literal expression. – codeshot – 8 years ago
1Can we assume that there will not be prepended or appended whitespace to the string and that whitespace is otherwise as specified in the sample input? That is, no whitespace between brackets, between the dot and the parameter name and other instances of whitespace is exactly 1 space. – JPvdMerwe – 15 years ago
@JPvdMerwe: Yes, good point, you may assume that. – sepp2k – 15 years ago
Are there any free variables? I mean variables unbound by a lambda like in the expression
(\y. a). – FUZxxl – 15 years agoAnother question: May I choose other names for the variables in the output? I mean
(\a. a)instead of (\b. b)`? – FUZxxl – 15 years agoAnd a third one: Can you make the varnames to one char each? This would make my solution shorter. – FUZxxl – 15 years ago
@FUZxxl: 1. There won't be any free variables in the input. 2. Yes, you may print any expression which is alpha-congruent to the expected output. 3. I think making the conditions easier after there already is a solution which works for the current specification would be unfair. Feel free to post both solutions, but as far as "winning" goes the one which can handle long variable names is the one that counts. – sepp2k – 15 years ago
Does the last example mean the interpreter has to support non-strict (a.k.a. lazy) evaluation? – Joey Adams – 15 years ago
@Joey: Yes, it should find a normal form for every expression which has one, so it can't use strict evaluation. – sepp2k – 15 years ago