n

n-1

n

n

Recall, the standard and the simplest representation of natural numbers is Church numerals, listed below. (As a common shorthand, we write cn for the term representing the number n .):

c0: \f\x.x c1: \f\x.f x c2: \f\x.f (f x) ...

cn

c(n+1)

succ:

\f\x. n f (f x) succ:

\f\x. f (n f x)

pred

pred cn

c(n-1)

pred c0

c0

The fundamental tautology of Church numerals is easy to overlook:

cn === cn succ c0

pn

pn: cn succp p0 (*)

succp

p0

pn

So far, the new numerals are the empty formality. They become useful once we relate them to Church numerals. For example, take p0 to be cm , for some m ; take p1 to be c(m+1) , etc. Clearly, succp is just the ordinary succ . Then pn has the meaning of c(n+m) ; definition (*) immediately gives the term for adding two numerals:

add:

\m.n succ m

To obtain the Kleene predecessor, let's think of pn as a point between two consecutive numbers cn and c(n-1) on the number line. It can be represented as a pair (c(n-1),cn) :

p0: (cm1, c0) p1: (c0, c1) p2: (c1, c2) ...

fst

snd

cm1

c0

cm1

c0

succp: \p.(snd p, succ (snd p))

pn

c(n-1)

pred:

.fst (n succp p0)



. n (\p s. s (p (\x y. y)) (\f x. f (p (\x y. y) f x))) (\s. s (\s z. z) (\s z. z)) (\x y. x)

succp

p0: Nothing or, desuraged: \g\y.y p1: Just c0 or, desuraged: \g\y.g (\f\x.x) p2: Just c1 or, desuraged: \g\y.g (\f\x.f x) ...

Maybe

succp

succp: \p.Just (p succ c0)

pred:

. (n succp p0) id c0



. n (\p g y. g (p (

f x. f (n f x)) (\s z. z))) (\g y. y) (\x. x) (\s z. z)

\f\x.

pred:

\f\x. (n (\p.Just (p f x)) p0) id x

c0

x

succ

f



f x. n (\p g y. g (p f x)) (\g y. y) (\y. y) x

There is more! What can be simpler than

p0: cm1 p1: c0 p2: c1, etc.

cm1

cn

cn (\z.false) false

false

cm1

cm1: \f\x.true

true

succp: \p.p (\z.false) false c0 (succ p)

pred:

.n succp (\f\x.true)



. n (\p. p (\z x y. y) (\x y. y) (\f x. x) (\f x. f (p f x))) (\f x u y. u)

cm1

cn

cn (\x.x)

\x.x

cm1

\x.c0

cm1: \f\x.c0 succp: \p.p (\x.x) (succ p)

pred:

.n (\p.p (\x.x) (succ p)) (\f\x.c0)



. n (\p. p (\x. x) (\f x. f (p f x))) (\f x s z. z)

Even the initial puzzle of predecessor as an un-application is solvable. To be sure, lambda calculus has no un-application primitive, letting us only apply terms but not examine them. However, lambda calculus can represent all computations, including itself. The representations can be examined and deconstructed to our heart's content. For example, consider

mynoapp: \x. \g\y. y x myapp: \f\x. \g\y. g f x

type myterm = App myterm myterm | NoApp term

cn

n

n

pn

pn: \f\x. cn (myapp f) (mynoapp x)

pn

reify cn

reify:

\f\x. n (myapp f) (mynoapp x)

cn

reflect

reflect: \p\f\x. fix (\s r. r (\f\x. f (s x)) (\y.y)) (p f x)

p3

reify c3

reflect p3

c3

let unapply = fun t -> match t of | App (_,x) -> x | x -> x

unapp: \t.t (\d m. m) (\d.t)

cn

pred:

. reflect (\f\x. (unapp ((reify n) f x)))

Thus, once again we have seen how important it is to contemplate representations and what they represent, and be able to move between the two. (Bonus to the reader who can find/make a Matrix quote.) Kleene predecessors gave us an example that canonical, canonized approaches are not the only ones possible -- and sometimes are not the best. Hence, no matter how much something appears settled, it is worth probing. Finally, one cannot help but wonder at the delicate behavior that arises from trite rules.