コンビネータ版チャーチ数の加減乗除

たった1つの関数から出発して昨日やったチャーチ数の加減乗除 - MEMO:はてな支店を再現する。
最初に生成している各種コンビネータはコメントをつけているが、m=のところからは基本的に昨日と同じなので省略。
最初の関数x(Fokker版の一点基底コンビネータ)だけは普通の関数定義だが、
それ以降は関数xの適用のみのため、unsafeCoerceもx内で1度使うだけで済む。

import Unsafe.Coerce
x f = f (\x y z -> x z $ y $ unsafeCoerce z) (\x y z -> x)

k  = x x            -- k  x y     = x
s  = x k            -- s  f g x   = f x (g x)
ki = s k            -- ki x y     = y
i  = ki x           -- i  x       = x
b  = s (k s) k      -- b  f g x   = f (g x)
d  = b b            -- d  f x g y = f x (g y)
c  = s (d s) (k k)  -- c  f x y   = f y x
t  = c i            -- t  x f     = f x 
v  = b c t          -- v  x y f   = f x y
s' = b (b s) b      -- s' f g h x = f (g x) (h x)
b' = b (b b) b      -- b' f g h x = f (g (h x))
c' = b (b c) b      -- c' f g x y = f (g y) x

m = s i i
y = b m (c b m)

true  = k
false = ki
not'  = v false true
and'  = c c false
or'   = t true

cons = v
car  = t true
cdr  = t false

inc  = s b
incs = s (b cons cdr) (b inc cdr)
dec  = b car (c (t incs) (k n0))

n0 = ki
n1 = i
n2 = inc n1
n4 = n2 n2
n5 = inc n4

add = b s d
mul = b
pow = t
sub = c (t dec)

is0 = v (k false) true
le  = d b is0 sub
ge  = c le
lt  = d b not' ge
gt  = c lt

rsub = c (c' c' s' sub) i     -- rsub f m n = f (sub m n) n
mod' = y $ b  (s' s (s' c lt i)) rsub
div' = y $ b' (s' s (c' c lt n0)) (d b inc) rsub

num n = n (+1) 0 :: Int
main = print $ map (\f -> num $ f n5 n2) [add,mul,pow,sub,mod',div']

実行結果
それぞれ5+2, 5*2, 5^2, 5-2, 5%2, 5/2の計算結果になっている。

[7,10,25,3,1,2]

おまけ

上記のソースは段階的に適用結果を変数に入れていてxだけで動いているという実感がわかないが、
例えばn2とmulをxだけで書くと以下のとおり。

import Unsafe.Coerce
x f = f (\x y z -> x z $ y $ unsafeCoerce z) (\x y z -> x)

n2  = x (x x) (x (x x) (x x (x (x x))) (x x)) (x (x x) (x x) x)
mul = x (x x) (x x (x (x x))) (x x)

num n = n (+1) 0 :: Int
main = print $ map num [n2,mul n2 n2,mul n2 (mul n2 n2)]

実行結果
それぞれ2, 2*2, 2*2*2の表示結果

[2,4,8]