3

I have a definition of the natural numbers in lambda calculus as follow, which was my main goal.

-- Apply a function n times on x
apply = \f -> \n -> \x -> foldr ($) x $ replicate n f

-- Church numbers
churchZero = \f -> id

churchOne = \f -> \x -> apply f 1 x

churchTwo = \f -> \x -> apply f 2 x

churchNatural = \n -> \f -> \x -> apply f n x

Then, the next step was defining the operators churchSum, churchMul and churchExp.

churchSum = \n -> \m -> \f -> \x -> n f (m f x)

churchMul = \n -> \m -> \f -> \x -> n (m f) x

churchExp = \n -> \m -> n m

Fine, it works, the first two functions are "easy" to deduce, but the exponentiation is not. For me at least. To understand a little bit more, i did the beta normalization on the lambda term:
(λf.λx. f(f x))(λf.λx f(f x)) to see that effectively the exponentiation is correct.

beta reduction

So, my question is: how could i deduce this lambda term for the exponentiation without known it? Even more, why something like λ> churchTwo churchTwo type checks on Haskell when the types are λ> churchTwo :: (b -> b) -> b -> b? Inside it do the beta normalization of the function?

David Lilue
  • 597
  • 2
  • 14
  • 1
    What exactly are you asking? This seems like more of a question for [CS.SE](https://cs.stackexchange.com/), and has not very much to do with Haskell. – leftaroundabout Nov 30 '17 at 00:37
  • `churchTwo churchTwo` typechecks because `(a -> a) -> (a -> a)` unifies with `b -> b` (in particular by the unifier `b ~ (a -> a)`). As it stands, this question is too broad, because the concept of an "easy" or "difficult" deduction is largely subjective. Each of the implementations of the three functions is deduced from its desired equational properties (for exponentiation, they are `x^0=1` and `x^(1+k)=x*x^k`). – user2407038 Nov 30 '17 at 01:41

1 Answers1

3

Your exp is a bit backwards:

((\f x -> f$f$f$x) `exp` (\f x -> f$f$x)) (+1) 0 == 8
--      3            ^          2                 = 8???
-- But 2^3 = 8

The correct(-er-ish) version would be

exp n m = m n
((\f x -> f$f$f$x) `exp` (\f x -> f$f$x)) (+1) 0 == 9
--      3            ^          2                 = 9

because it maintains the familiar order. This doesn't really affect how you might go around defining exp.


Exponentation is repeated multiplication: nm is n multiplied by itself m times. Church numerals represent repeated application of a function to a value. So, churchMul n is a function that multiplies a numeral by n, m is function that repeats a function m times, and churchOne is the base value (identity of multiplication). Put them together, then simplify:

-- n^m is the repeated multiplication of 1 by n, m times
exp n m = m (churchMul n) churchOne
-- expand definitions x2; simplify churchOne
exp n m = m (\o f x -> n (o f) x) (\f x -> f x)
-- eta contract x2
exp n m = m (\o f -> n (o f)) (\f -> f)
-- definition of (.), id
exp n m = m (\o -> n . o) id
-- eta contract
exp n m = m (n .) id
-- eta expand
exp n m f = m (n .) id f
-- Assume m has the type forall b. (b -> b) -> b -> b
-- This assumption may actually be false here, because the implicit type of exp
-- does not require that m, n have that type. The difference is that you could define
-- bogus _ _ _ = 0
-- (which isn't a church numeral) and still pass it to exp, which would no longer
-- act like exponentiation:
-- exp n bogus = bogus (n .) id = const 0
-- which also isn't a church numeral

-- Polymorphic functions like m give rise to theorems that can be derived
-- entirely from their types. I used http://www-ps.iai.uni-bonn.de/cgi-bin/free-theorems-webui.cgi
-- to get this one automatically.
-- Free theorem of the type of m
forall a b (g :: a -> b) (p :: a -> a) (q :: b -> b).
   (forall (x :: a). g (p x) = q (g x)) ->
     (forall (y :: a). g (m p y) = m q (g y))

-- Instantiate g = ($ f), p = (n .), q = n, y = id
  (forall x. (n . x) f = n (x f)) -> (m (n .) id f = m n f)

-- definition of (.)
  (n . x) f = n (x f)
-- so...
  m (n .) id f = m n f
-- transitive property
exp n m f = m n f
-- eta contract
exp n m = m n

The above stuff with the free theorem of m is really a rigorous version of the following argument (which probably translates better to the untyped lambda calculus):

-- m, being a valid numeral, is of the form
m f x = f $ f $ ... $ f $ f $ x

m (n .) id = (n .) $ (n .) $ ... $ (n .) $ (n .) $ id
           = (n .) $ (n .) $ ... $ (n .) $ n . id
           = (n .) $ (n .) $ ... $ (n .) $ n
           = (n .) $ (n .) $ ... $ n . n
           ...
           = n . n . ... . n . n
-- so
m n = n . n . ... . n . n = m (n .) id

As for why churchTwo churchTwo typechecks, note each occurrence in that expression has a different type, because churchTwo is polymorphic and describes an entire family of functions instead of just one.

-- most general type
churchTwo :: forall b. (b -> b) -> (b -> b)
-- Each occurrence of churchTwo can have a different type, so let's give them
-- different names.
-- I'm using underscores because these variables haven't been solved yet
churchTwo0 :: (_b0 -> _b0) -> (_b0 -> _b0)
churchTwo1 :: (_b1 -> _b1) -> (_b1 -> _b1)
churchTwo0 churchTwo1 :: _
-- Since churchTwo0 is being applied, the whole expression must have the
-- type on the right of the arrow
churchTwo0 churchTwo1 :: _b0 -> _b0
-- Since churchTwo0 is being applied to churchTwo1, the left side of the
-- top level arrow in churchTwo0 must be equal to the type of churchTwo1
(_b0 -> _b0) ~ ((_b1 -> _b1) -> (_b1 -> _b1))
-- Therefore...
(_b0 ~ (_b1 -> _b1))
churchTwo0 churchTwo1 :: (_b1 -> _b1) -> (_b1 -> _b1)
-- That's all the constraints we have, so replace the free variables
-- with universally quantified ones
chuchTwo0 churchTwo1 :: forall b. (b -> b) -> (b -> b)
-- (which is the type of a numeral)
HTNW
  • 27,182
  • 1
  • 32
  • 60