COMP3161/9164 Concepts of Programming Languages
Term 3, 2025

Church Encodings

These are the Church encodings and experiments that were typed into Haskell during the lecture.


type C_Bool a = (a -> a -> a)

church_true :: C_Bool a
church_true = (\a -> \b -> a)

church_false :: C_Bool a
church_false = (\a -> \b -> b)

church_and :: C_Bool (C_Bool a) -> C_Bool a -> C_Bool a
church_and = (\b1 b2 -> b1 b2 church_false)


-- Try β-normalising And T F?

-- church_and church_true church_false
--   ≡ (\b1 b2 -> b1 b2 (\a b -> b)) (\a b -> a) (\a b -> b)
--   β-> (\b2 -> (\a b -> a) b2 (\a b -> b)) (\a b -> b)
--   β-> (of b2)   (\a b -> a) (\a b -> b) (\a b -> b)
--   β-> (\b -> (\a b -> b)) (\a b -> b)
--   β-> (\a b -> b)  ≡ church_false



case_Bool :: Bool -> a -> a -> a
case_Bool x true_case false_case = case x of
  True -> true_case
  False -> false_case


f :: Bool -> Int
f x = case x of
  True -> 1
  False -> 2

f2 :: Bool -> Int
f2 x = case_Bool x 1 2




data Pet = Cat String | Dog String | Fish

case_pet :: Pet -> (String -> a) -> (String -> a) -> a -> a
case_pet x cat_case dog_case fish_case = case x of
  Cat nm -> cat_case nm
  Dog nm -> dog_case nm
  Fish -> fish_case

g :: Pet -> String
g x = case x of
  Cat nm -> ("Cat " ++ nm)
  Dog nm -> ("Doggie " ++ nm)
  Fish -> ("A Fish")



type C_Num a = (a -> a) -> a -> a


church_zero :: C_Num a
church_zero = \f x -> x

church_one :: C_Num a
church_one = \f x -> f x

church_two :: C_Num a
church_two = \f x -> f (f x)

church_suc :: C_Num a -> C_Num a
church_suc = \n f x -> f (n f x)


-- Try β-normalising Suc 1?

-- church_suc church_one
--   ≡ (\n f x -> f (n f x)) (\f x -> f x)
--   β-> (\f x -> f ((\f x -> f x) f x))
--   β-> (\f x -> f ((\x -> f x) x))
--   β-> (\f x -> f (f x))



church_suc2 :: C_Num a -> C_Num a
church_suc2 = \n f x -> n f (f x)


add :: C_Num a -> C_Num a -> C_Num a
add = \m n f x -> m f (n f x)

multiply :: C_Num (C_Num a) -> C_Num a -> C_Num a
multiply = \m n -> m (add n) church_zero

-- A student asks a great question: why does the type of the "m" parameter to
-- multiply need to be "C_Num (C_Num a)" rather than "C_Num a".
--
-- Simply, because n is being used to repeat actions on an unknown type "a",
-- but m is being used to repeat "add n", an operation on "C_Num a".
--
-- What we can't do (in Haskell's type system, unless we turn on some
-- extensions) is to say that m's type is "(∀t. C_Num t)". If we could say
-- that the m parameter is polymorphic to any instance of "C_Num t" then we
-- could avoid this complication, at the cost of needing more logic in our type
-- system.


church_to_int :: C_Num Int -> Int
church_to_int = \c -> c (\x -> x + 1) 0

print_church :: C_Num Int -> String
print_church = show . church_to_int

-- tests we can try now:
-- print_church church_one
-- print_church church_two
-- print_church (multiply (add church_one church_two) church_two)

2025-12-05 Fri 11:50

Announcements RSS