Haskell中的Lambda演算:有没有办法让教堂数字类型检查?

时间:2012-10-17 20:01:51

标签: haskell lambda-calculus church-encoding

我正在玩Haskell中的一些lambda演算,特别是教堂数字。我有以下定义:

let zero = (\f z -> z)
let one = (\f z -> f z)
let two = (\f z -> f (f z))
let iszero = (\n -> n (\x -> (\x y -> y)) (\x y -> x))
let mult = (\m n -> (\s z -> m (\x -> n s x) z))

这有效:

:t (\n -> (iszero n) one (mult one one))

发生检查失败:

:t (\n -> (iszero n) one (mult n one))

我用iszeromult玩了我的常数,他们似乎是正确的。有没有办法使这种类型?我不认为我在做什么太疯狂了,但也许我做错了什么?

2 个答案:

答案 0 :(得分:20)

在顶层看到你的定义是正确的,它们的类型也是正确的。问题在于,在第二个示例中,您使用n以两种不同的方式使用one - 或者更确切地说,它们的类型无法统一,并且尝试这样做会产生无限型。 (forall a. (a -> a) -> a -> a)的类似用法正常工作,因为每次使用都独立于不同类型。

要以简单明了的方式完成这项工作,您需要更高级别的多态性。教会数字的正确类型是RankNTypes,但不能推断出更高等级的类型,并且需要GHC扩展,例如newtype ChurchNum = ChurchNum (forall a. (a -> a) -> a -> a)。如果你启用了一个合适的扩展(在这种情况下你只需要排名-2,我认为)并为你的定义提供明确的类型,它们应该在不改变实际实现的情况下工作。

请注意,使用更高级别的多态类型存在(或至少存在)某些限制。但是,您可以将教会数字包裹在Num之类的内容中,这样也可以为他们提供{{1}}实例。

答案 1 :(得分:5)

让我们添加一些类型签名:

type Nat a = (a -> a) -> a -> a

zero :: Nat a
zero = (\f z -> z)

one :: Nat a
one = (\f z -> f z)

two :: Nat a
two = (\f z -> f (f z))

iszero :: Nat (a -> a -> a) -> a -> a -> a
iszero = (\n -> n (\x -> (\x y -> y)) (\x y -> x))

mult :: Nat a -> Nat a -> Nat a
mult = (\m n -> (\s z -> m (\x -> n s x) z))

正如您所看到的,除iszero的类型外,一切似乎都很正常。

让我们看看你的表情会发生什么:

*Main> :t (\n -> (iszero n) one n)
<interactive>:1:23:
    Occurs check: cannot construct the infinite type:
      a0
      =
      ((a0 -> a0) -> a0 -> a0)
      -> ((a0 -> a0) -> a0 -> a0) -> (a0 -> a0) -> a0 -> a0
    Expected type: Nat a0
      Actual type: Nat (Nat a0 -> Nat a0 -> Nat a0)
    In the third argument of `iszero', namely `(mult n one)'
    In the expression: (iszero n) one (mult n one)

了解错误如何使用我们的Nat别名!

我们实际上可以使用更简单的表达式\n -> (iszero n) one n获得类似的错误。这是错的。由于我们正在调用iszero n,因此我们必须n :: Nat (b -> b -> b)。此外,由于iszero s类型,第二个和第三个参数none必须具有b类型。现在我们有两个n类型的方程式:

n :: Nat (b -> b -> b)
n :: b

哪个不甘心。长号。