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)
wherex
could be any legal variable name ande
any legal expression. Herex
is called the parameter ande
is 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
x
currently 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)
wheref
anda
are legal expressions. Heref
is called the function anda
is called the argument. - A variable has the form
x
wherex
is 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 – 2015-09-27T20:35:03.033
@Kaseorg in ((λ f. (λ x. (f x))) (λ y. (λ x. y))) what is the result? is it (λ x. (λ z. x))? what is z? – RosLuP – 2016-09-05T00:42:36.383
what is the result of "(λ a. ((( w(a) ))) )z" "w(z)" "(w(z))" or "((( w(z) )))" ? – RosLuP – 2016-09-05T01:28:41.523
the lambda program allow the result of (/x.(/y. x^2+y^2)3)7 to be 7^2+3^2? – RosLuP – 2016-09-05T09:06:23.673
@RosLuP The correct result is
(λ x. (λ z. x))
because the function should return the outer function's argument, not the inner function's argument.z
is a new identifier that was chosen overx
, becausex
would shadow the outerx
, leading to a wrong result. Instead ofz
any other valid identifier could also have chosen.(λ a. ((( w(a) ))) )z
is 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 – 2016-09-05T12:22:49.733@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 – 2016-09-05T12:24:38.183(w z) has parentesis different from (λ x. (λ z. x)) so it would be [λ x. [λ z. x]] – RosLuP – 2016-09-06T22:13:20.283
@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 – 2016-09-06T22:20:36.083i'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 – 2016-09-06T23:07:19.020
@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 – 2016-09-06T23:15:21.750... for the lambda calculus uses round parentheses everywhere and that's what this challenge asks for. – sepp2k – 2016-09-06T23:16:40.927
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 – 2016-09-16T00:56:04.767
@RosLup. The presence of a lambda indicates whether an expression is a macro or a literal expression. – codeshot – 2018-03-31T14:04:09.860
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 – 2011-01-31T15:42:33.003
@JPvdMerwe: Yes, good point, you may assume that. – sepp2k – 2011-01-31T15:47:38.633
Are there any free variables? I mean variables unbound by a lambda like in the expression
(\y. a)
. – FUZxxl – 2011-02-01T09:46:22.130Another question: May I choose other names for the variables in the output? I mean
(\a. a)
instead of (\b. b)`? – FUZxxl – 2011-02-01T09:48:12.800And a third one: Can you make the varnames to one char each? This would make my solution shorter. – FUZxxl – 2011-02-01T10:03:45.597
@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 – 2011-02-01T13:21:25.660
Does the last example mean the interpreter has to support non-strict (a.k.a. lazy) evaluation? – Joey Adams – 2011-02-01T17:41:16.287
@Joey: Yes, it should find a normal form for every expression which has one, so it can't use strict evaluation. – sepp2k – 2011-02-01T18:05:24.730