Haskell中教堂数字的减法
我正在尝试在 Haskell 中实现教堂数字,但遇到了一个小问题。 Haskell 通过 Occurs 检查抱怨无限类型
:无法构造无限类型: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
I'm attempting to implement church numerals in Haskell, but I've hit a minor problem. Haskell complains of an infinite type with
Occurs check: cannot construct the infinite type: t = (t -> t1) -> (t1 -> t2) -> t2
when I try and do subtraction. I'm 99% positive that my lambda calculus is valid (although if it isn't, please tell me). What I want to know, is whether there is anything I can do to make haskell work with my functions.
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
问题是
predChurch
的多态性太强,无法通过 Hindley-Milner 类型推断正确推断。例如,很容易写成:但这种类型是不正确的。
Church a
将a -> 作为其第一个参数。 a
,但您传递的是n
一个两个参数的函数,显然是类型错误。问题在于
Church a
无法正确表征 Church 数字。 Church 数字仅代表一个数字——该类型参数到底意味着什么?例如:可以进行类型检查,但
foo
肯定不是 Church 数字。我们需要限制类型。教堂数字需要适用于任何a
,而不仅仅是特定的a
。正确的定义是:您需要在文件顶部添加
{-# LANGUAGE RankNTypes #-}
才能启用此类类型。现在我们可以给出我们期望的类型签名:
您必须在这里给出类型签名,因为 Hindley-Milner 无法推断出更高级别的类型。
然而,当我们去实现
subChurch
时,另一个问题出现了:我不是 100% 确定为什么会发生这种情况,我认为
forall
被类型检查器展开得过于自由。不过,这并不令我感到惊讶;较高级别的类型可能有点脆弱,因为它们给编译器带来了困难。此外,我们不应该使用type
作为抽象,我们应该使用newtype
(这让我们在定义上更加灵活,帮助编译器进行类型检查,并标记我们使用抽象实现的位置):并且我们必须修改
predChurch
以根据需要滚动和展开:与
subChurch
相同:但我们不需要类型不再需要签名——滚动/展开中有足够的信息来再次推断类型。
在创建新抽象时,我总是推荐
newtype
。常规type
同义词在我的代码中非常罕见。The problem is that
predChurch
is too polymorphic to be correctly inferred by Hindley-Milner type inference. For example, it is tempting to write:but this type is not correct. A
Church a
takes as its first argument ana -> a
, but you are passingn
a two argument function, clearly a type error.The problem is that
Church a
does not correctly characterize a Church numeral. A Church numeral simply represents a number -- what on earth could that type parameter mean? For example:That typechecks, but
foo
is most certainly not a Church numeral. We need to restrict the type. Church numerals need to work for anya
, not just a specifica
. The correct definition is:You need to have
{-# LANGUAGE RankNTypes #-}
at the top of the file to enable types like this.Now we can give the type signature we expect:
You must give a type signature here because higher-rank types are not inferrable by Hindley-Milner.
However, when we go to implement
subChurch
another problem arises:I am not 100% sure why this happens, I think the
forall
is being too liberally unfolded by the typechecker. It doesn't surprise me though; higher rank types can be a bit brittle because of the difficulties they present to a compiler. Besides, we shouldn't be using atype
for an abstraction, we should be using anewtype
(which gives us more flexibility in definition, helps the compiler with typechecking, and marks the places where we use the implementation of the abstraction):And we have to modify
predChurch
to roll and unroll as necessary:Same with
subChurch
:But we don't need type signatures anymore -- there is enough information in the roll/unroll to infer types again.
I always recommend
newtype
s when creating a new abstraction. Regulartype
synonyms are pretty rare in my code.predChurch
的这个定义在简单类型的 lambda 中不起作用微积分,仅在非类型化版本中。您可以找到适用于 Haskell 的predChurch
版本在这里。This definition of
predChurch
doesn't work in simply typed lambda calculus, only in the untyped version. You can find a version ofpredChurch
that works in Haskell here.我也遇到过同样的问题。我在没有添加类型签名的情况下解决了这个问题。
这是解决方案,其中
cons
car
复制自 SICP。您可以在此处找到完整源代码。
写完
sub mn = n pred m
后,我真的很惊讶,并将其加载到 ghci 中,没有类型错误!Haskell 代码是如此简洁! :-)
I have encountered the same problem. And I solved it without adding type signature.
Here's the solution, with
cons
car
copied from SICP.You can find full source here.
I'm really amazed after writing
sub m n = n pred m
, and load it in ghci without type error!Haskell code is so concise! :-)