我正在尝试在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
答案 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 here的predChurch
版本。
答案 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代码非常简洁! : - )