0

I was trying to implement the Church numeral predecessor function pred, then I referred to the wikipedia page about church encodings.

According to it I wrote the following

{-# LANGUAGE ScopedTypeVariables, RankNTypes #-}
module Church where

newtype Church = Church { runChurch :: forall a. (a -> a) -> a -> a }

pred1 :: Church -> Church
pred1 (Church n) = Church (\f a -> extract (n (\g h -> h (g f)) (const a))) where
  extract k = k id

which type checks.

But when I tried to use pred1 to implement it more directly instead

pred2 :: forall a. ((a -> a) -> a -> a) -> (a -> a) -> a -> a
pred2 n = runChurch $ pred1 (Church n)

ghc complains that

    • Couldn't match type ‘a1’ with ‘a’
      ‘a1’ is a rigid type variable bound by
        a type expected by the context:
          forall a1. (a1 -> a1) -> a1 -> a1
        at Church.hs:11:30-37
      ‘a’ is a rigid type variable bound by
        the type signature for:
          pred2 :: forall a. ((a -> a) -> a -> a) -> (a -> a) -> a -> a
        at Church.hs:10:1-61
      Expected type: (a1 -> a1) -> a1 -> a1
        Actual type: (a -> a) -> a -> a
    • In the first argument of ‘Church’, namely ‘n’
      In the first argument of ‘pred1’, namely ‘(Church n)’
      In the second argument of ‘($)’, namely ‘pred1 (Church n)’
    • Relevant bindings include
        n :: (a -> a) -> a -> a (bound at Church.hs:11:7)
        pred2 :: ((a -> a) -> a -> a) -> (a -> a) -> a -> a
          (bound at Church.hs:11:1)
   |
11 | pred2 n = runChurch $ pred1 (Church n)
   |                                     ^

or the lambda calculus style

pred3 :: forall a. ((a -> a) -> a -> a) -> (a -> a) -> a -> a
pred3 n f a1 = extract (n (\g h -> h (g f)) (const a1)) where
  extract k = k id

the compiler says

    • Occurs check: cannot construct the infinite type:
        a ~ (a0 -> a0) -> a
    • In the first argument of ‘extract’, namely
        ‘(n (\ g h -> h (g f)) (const a1))’
      In the expression: extract (n (\ g h -> h (g f)) (const a1))
      In an equation for ‘pred3’:
          pred3 n f a1
            = extract (n (\ g h -> h (g f)) (const a1))
            where
                extract k = k id
    • Relevant bindings include
        a1 :: a (bound at Church.hs:14:11)
        f :: a -> a (bound at Church.hs:14:9)
        n :: (a -> a) -> a -> a (bound at Church.hs:14:7)
        pred3 :: ((a -> a) -> a -> a) -> (a -> a) -> a -> a
          (bound at Church.hs:14:1)
   |
14 | pred3 n f a1 = extract (n (\g h -> h (g f)) (const a1)) where
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^

if I don't specify the type of pred3, the inferred type is

pred3 :: (((t1 -> t2) -> (t2 -> t3) -> t3) -> (b -> a1) -> (a2 -> a2) -> t4)
    -> t1 -> a1 -> t4

I can't figure these two errors out, any suggestions would be helpful

farmerzhang1
  • 41
  • 1
  • 4
  • 1
    `Church -> Church` means `(forall a . F a) -> (forall a . F a)` (for a suitable `F`), while the latter attempts use `forall a . F a -> F a` which is a completely different type. At the very least, you need the same (rank-2) type. When you call `Church n` in your code the type is only `F a` for a fixed (rigid) `a` instead of the needed `forall a. F a`. – chi Oct 03 '21 at 16:05

1 Answers1

3

Imagine someone tried to call pred2 @Int. Then you'd end up trying to stuff an (Int -> Int) -> Int -> Int into a Church, which is obviously wrong. To make pred2 work, you need to make sure its first argument is always a polymorphic function, which means pred2 needs to have a rank-2 type. Its definition is fine, so just replace its type signature with this:

pred2 :: (forall a. (a -> a) -> a -> a) -> (b -> b) -> b -> b

The same applies to pred3.