The Bluff combinator This file gives a solution to a Bluff combinator problem in pure untyped lambda-calculus. The problem is due to Mayer Goldberg and is about ten years old: Let c_i denote a Church numeral for the number i. Design two terms 'beq' and 'bluff' so that the following reductions hold: beq c_i c_j ==> \x\y.x (that is, boolean 'true') iff i == j ==> \x\y.y (that is, boolean 'false') otherwise beq bluff c_i ==> \x\y.x (that is, boolean 'true') for all i beq c_i bluff ==> \x\y.x (that is, boolean 'true') for all i beq bluff bluff ==> \x\y.y (that is, boolean 'false') In the following, we will write 'true' for the combinator \x\y.x and 'false' for the combinator \x\y.y . It appears that the gist of the problem is discriminating _a_ Church numeral from some other term. We turn for help to the following equivalence, which is valid for all Church numerals: c_i (\x.false) false <==> false for all i Therefore, to tell a Church numeral from a bluff combinator, we need to design 'bluff' so that bluff (\x.false) false ==> true The simplest such term is bluff ::= \f\x.true This term indeed looks like it is bluffing: it says 'true' no matter what it is applied to. Let us see how this works. We will be using a lambda-calculator written in Haskell: http://pobox.com/~oleg/ftp/Haskell/Lambda_calc.lhs We assume that the above file is downloaded into the current directory, under the name Lambda_calc.lhs . We also need the definitions of basic lambda-calculus terms: http://pobox.com/~oleg/ftp/Haskell/LC_basic.lhs In addition, we need a file http://pobox.com/~oleg/ftp/Haskell/LC_Pnum.lhs that defines a symmetric difference of two Church numerals. We need the latter for a term that compares two Church numerals for equality. The symmetric difference function and the equality predicate run in _linear_ time, with respect to the magnitude of the largest numeral in question. This linearity property is the consequence of using P-numerals internally. The latter permit zero checking, successor and _predecessor_ operations in constant time (given the normal order of evaluation). This message is a literate Haskell script. It could be executed by loading it into Hugs (again, providing that the current directory contains files Lambda_calc.lhs, LC_basic.lhs, and LC_Pnum.lhs). First, we load the calculator and the definitions of common lambda terms (such as true, false, lif, and cons), as well as the definitions of the Church numerals (constants c0, c1, c2 and c3 and the term succ). > import Prelude hiding ((^),succ,not,or,and) > import Lambda_calc > import LC_basic > import LC_Pnum(diffs) -- import symmetric difference term Note that x ^ t denotes (\x . t) and x # y stands for an application of x to y. As we said earlier, true stands for \x\y.x and false stands for \x\y.y. We can see that by asking the calculator (Hugs, actually) to show these terms: Main> print true (\x. (\y. x)) Main> print false (\x. (\y. y)) Main> print lif -- 'if' in lambda-calculus (\p. (\x. (\y. p x y))) Let us first define variables with mnemonic names > [ci,cj] = > map make_var ["ci","cj"] The symmetric difference of two Church numerals is defined in the module LC_Pnum. We can use the difference combinator to derive the equality predicate for two Church numerals: > ceq = ci ^ cj ^ zerop # (diffs # ci # cj) Let us test the equality predicate: Main> eval $ ceq # c0 # c0 (\x. (\y. x)) -- which is true Main> eval $ ceq # c0 # c1 (\x. (\y. y)) -- which is false Main> eval $ ceq # c1 # c0 (\x. (\y. y)) -- which is false again Main> eval $ ceq # c2 # c2 (\x. (\y. x)) -- which is true Now we can define the bluff combinator: > bluff = f ^ x ^ true and the beq combinator: > beq = a ^ b ^ > lif # (bluffp # a) > -- a is the bluff combinator > # (not # (bluffp # b)) -- false if b is bluff, true otherwise > -- a is not bluffing: it's truly a Church numeral > # (lif # (bluffp # b) > # true -- 'b' is a bluff, but 'a' isn't > # (ceq # a # b) -- both a and b are Church numerals > ) > where > bluffp = c ^ (c # (x ^ false) # false) To assure the reader that the above is a term in pure lambda-calculus, we will print it out: Main> putStr $ show_term beq 100 (\a. (\b. (\p. (\x. (\y. p x y))) ((\c. c (\x. (\x. (\y. y))) (\x. (\y. y))) a) ((\p. p (\x. (\y. y)) (\x. (\y. x))) ((\c. c (\x. (\x. (\y. y))) (\x. (\y. y))) b)) ((\p. (\x. (\y. p x y))) ((\c. c (\x. (\x. (\y. y))) (\x. (\y. y))) b) (\x. (\y. x)) ((\ci. (\cj. (\c. c ((\p. (\q. p q (\x. (\y. y)))) (\x. (\y. y))) (\x. (\y. x))) ((\ck. (\cl. (\arg. (\pn. pn (\pn. (\seed. (\c. (\f. (\x. f (c f x)))) seed)) (\f. (\x. x))) ((\p. p (\x. (\y. y))) arg)) (ck (\arg. (\ud_flag. (\pn. (\p. (\x. (\y. p x y))) ud_flag ((\x. (\y. (\p. p x y))) (\x. (\y. x)) ((\pn. (\f. (\x. f pn (pn f x)))) pn)) (pn (\prev. (\_. (\x. (\y. (\p. p x y))) ud_flag prev)) ((\x. (\y. (\p. p x y))) (\x. (\y. x)) ((\pn. (\f. (\x. f pn (pn f x)))) (\f. (\x. x))))))) ((\p. p (\x. (\y. x))) arg) ((\p. p (\x. (\y. y))) arg)) ((\x. (\y. (\p. p x y))) (\x. (\y. y)) ((\cn. cn (\pn. (\f. (\x. f pn (pn f x)))) (\f. (\x. x))) cl))))) ci cj))) a b)))) The argument 100 of show_term is the maximal depth of the term to print. We can now test that the 'beq' and 'bluff' combinator solve the problem: Main> eval $ beq # c0 # c0 (\x. (\y. x)) -- which is true Main> eval $ beq # c0 # c1 (\x. (\y. y)) -- which is false Main> eval $ beq # c1 # c1 (\x. (\y. x)) -- which is true Main> eval $ beq # c1 # bluff (\x. (\y. x)) -- which is true Main> eval $ beq # c0 # bluff (\x. (\y. x)) -- which is true Main> eval $ beq # bluff # c1 (\x. (\y. x)) -- which is true Main> eval $ beq # bluff # bluff (\x. (\y. y)) -- which is false