【问题标题】:Subtraction of church numerals in haskellHaskell中教堂数字的减法
【发布时间】:2011-09-29 13:25:28
【问题描述】:

我正在尝试在 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

【问题讨论】:

  • 您应该进行类型声明type Church a = (a -> a) -> a -> a。它更干净,没有什么不同。
  • 另请注意,写出类型签名会很有帮助。它会告诉你问题出在哪里……
  • 我最终删除了类型签名,看看 ghci 是否可以正确推断它们,并希望摆脱错误(错误没有改变)......而且,我更喜欢括号周围类型。它让我更加突出

标签: haskell lambda-calculus church-encoding


【解决方案1】:

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

这是解决方案,cons carSICP 复制而来。

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 代码如此简洁! :-)

【讨论】:

  • 这真的行不通。如果您查看 GHCi 中的推断类型,它们太专业了,例如showChurch $ sub (plus three two) two 给出类型错误。
  • @hammar 哎呀,你是对的。我只测试了sub two onesub three two 给出类型错误。
【解决方案2】:

问题在于 predChurch 多态性无法通过 Hindley-Milner 类型推断正确推断。例如,很想写:

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

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

问题在于Church a 没有正确表征教堂数字。 Church 数字仅代表一个数字——该类型参数到底意味着什么?例如:

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'

我不是 100% 确定为什么会发生这种情况,我认为 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

但我们不再需要类型签名——roll/unroll 中有足够的信息来再次推断类型。

在创建新的抽象时,我总是推荐newtypes。常规的type 同义词在我的代码中非常少见。

【讨论】:

  • 至于 type 错误,这是因为在 Haskell 中,多态类型必须仅使用单态类型参数进行实例化:在 type Church = forall a. (a -> a) -> (a -> a) 中,类型变量 a 必须是单态的,但在 subChurch定义并非如此(在(n predChurch) 类型变量a 设置为Church,这是多态的)。这里有详细解释:okmij.org/ftp/Haskell/types.html#some-impredicativity
【解决方案3】:

predChurchdoesn't work in simply typed lambda calculus的这个定义,只在无类型版本中。你可以找到在 Haskell here 中工作的 predChurch 版本。

【讨论】:

  • 谢谢,这就是我正在寻找的答案。我只是想知道是否有某种魔法可以让 haskell 不关心类型。我已经有一个在haskell 中工作的定义,我只是想知道我是否可以让无类型版本在haskell 中工作。再次感谢。
  • @Probie:请记住,第一位仅指简单类型的 λ 演算,它类似于 Haskell,没有任何:多态类型、类型类、data 和 @987654326 @ 和递归绑定。
猜你喜欢
  • 2011-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多