我正在尝试通过给出类似这样的类型来研究Haskell中的教会数字,并认为自然数n
基本上是将以下类型的函数应用于值的表达式t
次输入n
。
type Nat = forall t. (t -> t) -> t -> t
有了这个想法,我可以通过以下方式定义zero
,successor
,plus
,mult
:
zero :: Nat
zero = \f t -> t
succ :: Nat -> Nat
succ n = \f -> f . (n f)
plus :: Nat -> Nat -> Nat
plus m n = \f -> (m f) . (n f)
mult :: Nat -> Nat -> Nat
mult m n = \f -> m (n f) -- Equal to \f -> (m . n) f
-- Pointfree version
mult' :: Nat -> Nat -> Nat
mult' = (.)
当我尝试定义取幂时,我想尝试应用允许我定义乘法的相同推理,即将mult m
n
次应用于1
< /强>
这导致以下代码
exp' :: Nat -> Nat -> Nat
exp' m n = n (mult m) (succ zero)
但是这样做会检查来自GHC的以下错误:
Couldn't match type ‘t’ with ‘t1’
‘t’ is a rigid type variable bound by
the type signature for:
exp' :: forall t. Nat -> Nat -> (t -> t) -> t -> t
at church.lhs:44:3
‘t1’ is a rigid type variable bound by
a type expected by the context:
forall t1. (t1 -> t1) -> t1 -> t1
at church.lhs:44:17
Expected type: ((t -> t) -> t -> t) -> (t -> t) -> t -> t
Actual type: Nat -> Nat
错误似乎表明类型检查器没有正确地实例化n
的类型,理想情况下,类型t
应该用另一个(t -> t
)实例化以使表达式通过。
令我困惑的是以下代码类型检查:
exp :: Nat -> Nat -> Nat
exp m n = n ((.) m) (succ zero) -- replace mult by (.)
有人会介意解释这里的问题吗?为什么exp'
的第一个定义不是类型检查而是第二个exp
类型检查?
谢谢!
答案 0 :(得分:7)
它不起作用的原因是它涉及几个不可预测的实例化,这在Haskell中通常是不允许的。如果你打开*Error: s = separate(predicate.is_positive,[1, -3, -2, 4, 0, -1, 8]) raised exception TypeError: 'int' object is not iterable
,你可以编译它:
-XImpredicativeTypes
第二个版本类型检查因为{-# LANGUAGE ImpredicativeTypes #-}
...
exp' :: Nat -> Nat -> Nat
exp' m n = n (mult m) (succ zero)
具有更高的排名类型,即使它在定义上等于mult'
,因此类型检查的进展也不同。由于(.)
的类型更简单(排名1),因此类型检查会更频繁地成功。
GHC文档警告(.)
不起作用,所以我要小心不要使用它。绕过它的典型方法是简单地使用ImpredicativeTypes
:
newtype
要查看实际操作中的impredicative实例,可以使用类型化的洞:
newtype Nat' = N { instN :: Nat }
exp'' :: Nat -> Nat -> Nat
exp'' m n = instN $ n (\(N q) -> N $ mult m q) (N $ succC zero)
这将报告exp' :: Nat -> Nat -> Nat
exp' m n = _ (mult m) (succC zero)
类型,与forall a . (Nat -> Nat) -> Nat -> (a -> a) -> a -> a
相同。由于您将(Nat -> Nat) -> Nat -> Nat
放在那里,因此必须将此类型与n
统一,这涉及使用多边形forall a . (a -> a) -> a -> a
实例化类型变量a
。