在haskell中减去教堂数字

时间:2011-07-06 11:38:51

标签: haskell lambda-calculus church-encoding

我正在尝试在Haskell中实现教堂数字,但我遇到了一个小问题。 Haskell抱怨

的无限类型

发生检查:无法构造无限类型:t =(t - > t1) - > (t1 - > t2) - > T2

当我尝试做减法时。我99%肯定我的lambda演算是有效的(虽然如果不是,请告诉我)。我想知道的是,我能做些什么来让haskell与我的功能一起工作。

module Church where

type (Church a) = ((a -> a) -> (a -> a))

makeChurch :: Int -> (Church a)
makeChurch 0 = \f -> \x -> x
makeChurch n = \f -> \x -> f (makeChurch (n-1) f x)

numChurch x = (x succ) 0

showChurch x = show $ numChurch x

succChurch = \n -> \f -> \x -> f (n f x)

multChurch = \f2 -> \x2 -> \f1 -> \x1 -> f2 (x2 f1) x1

powerChurch = \exp -> \n -> exp (multChurch n) (makeChurch 1)

predChurch = \n -> \f -> \x -> n (\g -> \h -> h (g f)) (\u -> x) (\u -> u)

subChurch = \m -> \n -> (n predChurch) m

3 个答案:

答案 0 :(得分:27)

问题是predChurch 太多态是由Hindley-Milner类型推断正确推断出来的。例如,写下来很有吸引力:

predChurch :: Church a -> Church a
predChurch = \n -> \f -> \x -> n (\g -> \h -> h (g f)) (\u -> x) (\u -> u)

但这种类型不正确。 Church a将第一个参数作为a -> a,但是您传递n两个参数函数,显然是类型错误。

问题在于Church a无法正确表征教会数字。教会数字只代表一个数字 - 这个类型参数究竟意味着什么?例如:

foo :: Church Int
foo f x = f x `mod` 42

那种类型检查,但foo肯定不是教会数字。我们需要限制类型。教会数字需要适用于任何 a,而不仅仅是特定的a。正确的定义是:

type Church = forall a. (a -> a) -> (a -> a)

您需要在文件顶部放置{-# LANGUAGE RankNTypes #-}才能启用此类型。

现在我们可以给出我们期望的类型签名:

predChurch :: Church -> Church
-- same as before

必须在这里提供类型签名,因为Hindley-Milner无法推断出更高等级的类型。

但是,当我们去实施subChurch时,会出现另一个问题:

Couldn't match expected type `Church'
       against inferred type `(a -> a) -> a -> a'

我不是百分之百确定为什么会发生这种情况,我认为forall太过自由地展开了类型搜索。不过我并不感到惊讶;较高级别的类型可能有点脆弱,因为它们给编译器带来了困难。此外,我们不应该使用type进行抽象,我们应该使用newtype(这使我们在定义方面更灵活,帮助编译器进行类型检查,并标记我们使用抽象实现的地方:

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

我们必须修改predChurch以根据需要滚动和展开:

predChurch = \n -> Church $ 
    \f -> \x -> unChurch n (\g -> \h -> h (g f)) (\u -> x) (\u -> u)

subChurch相同:

subChurch = \m -> \n -> unChurch n predChurch m

但是我们不再需要类型签名了 - 滚动/展开中有足够的信息来再次推断类型。

在创建新抽象时,我总是建议newtype。在我的代码中,常规type个同义词非常罕见。

答案 1 :(得分:5)

predChurch doesn't work in simply typed lambda calculus的定义,仅限于无类型版本。您可以找到适用于Haskell herepredChurch版本。

答案 2 :(得分:-1)

我遇到了同样的问题。我在没有添加类型签名的情况下解决了它。

以下是解决方案,其中cons car已从SICP复制。

cons x y = \m -> m x y
car z = z (\p q -> p)
cdr z = z (\p q -> q)

next z = cons (cdr z) (succ (cdr z))
pred n = car $ n next (cons undefined zero)

sub m n = n pred m

您可以找到完整的来源here

写完sub m n = n pred m后我真的很惊讶,并在没有类型错误的情况下将其加载到ghci中!

Haskell代码非常简洁! : - )