Haskell中教堂数字的减法

发布于 2024-11-18 23:24:03 字数 831 浏览 2 评论 0原文

我正在尝试在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

始终不够 2024-11-25 23:24:03

问题是 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 数字。 Church 数字仅代表一个数字——该类型参数到底意味着什么?例如:

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

可以进行类型检查,但 foo 肯定不是 Church 数字。我们需要限制类型。教堂数字需要适用于任何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

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

在创建新抽象时,我总是推荐 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:

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

but this type is not correct. A Church a takes as its first argument an a -> a, but you are passing n 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:

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

That typechecks, but foo is most certainly not a Church numeral. We need to restrict the type. Church numerals need to work for any a, not just a specific a. The correct definition is:

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

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:

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

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:

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

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 a type for an abstraction, we should be using a newtype (which gives us more flexibility in definition, helps the compiler with typechecking, and marks the places where we use the implementation of the abstraction):

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

And we have to modify predChurch to roll and unroll as necessary:

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

Same with subChurch:

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

But we don't need type signatures anymore -- there is enough information in the roll/unroll to infer types again.

I always recommend newtypes when creating a new abstraction. Regular type synonyms are pretty rare in my code.

凤舞天涯 2024-11-25 23:24:03

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 of predChurch that works in Haskell here.

來不及說愛妳 2024-11-25 23:24:03

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

这是解决方案,其中 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

您可以在此处找到完整源代码。

写完 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.

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

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! :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文