初步说明:鉴于此处提供的证据,我假设您正在使用:
type GenericQ r = forall a . Data a => a -> r
从syb和
gmapQ :: Data a => (forall d. Data d => d -> u) -> a -> [u]
从Data.Data
.
如果我对此有误,请告诉我。此外,forall
下文中的任何 s 都将被明确写入。
这里有很多东西。正如夏立耀所暗示的,这是一个涉及类型的概括问题op
。关于您对 的第一个定义,有三个相关事实shallowest
:
在泛化之前,推断的类型op
是Data d => d -> f a
。鉴于Data d
约束,单态限制的规则 1(参见报告的第 4.5.5 小节)意味着d
这种类型不能被概括。
在 的正文中shallowest
,op
出现在两个地方。第一个是op z
,z :: a1
在顶层被 的签名约束和约束shallowest
。结果是,这种出现不需要op
参数类型的泛化:就它而言,类型可以是,类型变量中的单态(我从报告的第 4.5.4 小节中采用了这个术语)。op
forall f a. a1 -> f a
a1
然而,另一个事件是gmapQ op z
。gmapQ
具有 rank-2 类型,需要多态参数。既然如此,这种情况需要对 的论点类型进行概括op
,正如夏立耀的回答末尾所指出的那样。
#1 和 #3 是相互矛盾的要求,因此您会遇到类型错误,这可以通过禁用单态限制或通过要求op
对带有签名的参数类型进行多态来避免。由于op
#2 中描述的另一个事件,这种情况被报告为涉及这两个事件的不匹配。
下面是一个更简单的扩展示例,它可能有助于了解发生了什么。(如果您要将以下代码片段放入 GHCi,此外-XRankNTypes
您还应该设置-XMonomorphismRestriction
and-XNoExtendedDefaultRules
以查看相同的结果。)
这是一个 rank-2 类型的函数,它将扮演以下角色gmapQ
:
glub :: (forall x. Show x => x -> String) -> String
glub f = f 7
现在让我们尝试一个类似于shallowest
...的场景
foo1 :: forall a. Show a => a -> String
foo1 x = bar x ++ glub bar
where
bar = show
...还有你的错误:
<interactive>:506:23: error:
• Couldn't match type ‘x’ with ‘a’
‘x’ is a rigid type variable bound by
a type expected by the context:
forall x. Show x => x -> String
at <interactive>:506:18-25
‘a’ is a rigid type variable bound by
the type signature for:
foo1 :: forall a. Show a => a -> String
at <interactive>:505:1-38
Expected type: x -> String
Actual type: a -> String
• In the first argument of ‘glub’, namely ‘bar’
In the second argument of ‘(++)’, namely ‘glub bar’
In the expression: bar x ++ glub bar
• Relevant bindings include
bar :: a -> String (bound at <interactive>:508:3)
x :: a (bound at <interactive>:506:5)
foo1 :: a -> String (bound at <interactive>:506:1)
在应该去的签名处添加一个通配符bar
会产生一个额外的错误,这个错误更具暗示性:
foo2 :: forall a. Show a => a -> String
foo2 x = bar x ++ glub bar
where
bar :: _
bar = show
• Found type wildcard ‘_’ standing for ‘a -> String’
Where: ‘a’ is a rigid type variable bound by
the type signature for:
foo2 :: forall a. Show a => a -> String
at <interactive>:511:1-38
To use the inferred type, enable PartialTypeSignatures
• In the type signature: bar :: _
In an equation for ‘foo2’:
foo2 x
= bar x ++ glub bar
where
bar :: _
bar = show
• Relevant bindings include
x :: a (bound at <interactive>:512:5)
foo2 :: a -> String (bound at <interactive>:512:1)
请注意通配符“代表a -> String
”是如何被声明为一个独立的事实,a
而不是受foo2
. 我相信这对应于我在上面第 2 点中提到的类型变量中的单态和多态之间的区别。
提供bar
多态类型签名使其工作:
foo3 :: forall a. Show a => a -> String
foo3 x = bar x ++ glub bar
where
bar :: forall b. Show b => b -> String
bar = show
使 bar 的定义变得有意义也是如此,它通过使其成为“函数绑定”而不是“简单模式绑定”来规避单态性限制:
foo4 :: forall a. Show a => a -> String
foo4 x = bar x ++ glub bar
where
bar x = show x
为了完整起见,值得注意的是,没有类型限制意味着没有单态限制:
foo5 :: forall a. Show a => a -> String
foo5 x = bar x ++ glub bar
where
bar = const "bar"
相关情况涉及使用bar
两次,但没有 rank-2 函数:
foo6 x y = bar x ++ bar y
where
bar = show
GHC 会推断出哪种类型foo6
?
GHCi> :t foo6
foo6 :: Show a => a -> a -> [Char]
参数获得相同的类型,否则需要对 进行泛化bar
,这需要类型签名(或 pointfullness 等):
foo7 x y = bar x ++ bar y
where
bar :: forall a. Show a => a -> String
bar = show
GHCi> :t foo7
foo7 :: (Show a1, Show a2) => a1 -> a2 -> [Char]
由于我还没有提到它,这里是你的第二个类似物shallowest
:
foo8 :: forall a. Show a => a -> String
foo8 x = bar x
where
bar = show
值得强调的bar
是,这里实际上并没有被概括:它在类型变量中是单态的a
。我们仍然可以通过弄乱foo7
而不是使用来打破这个例子bar
:
foo9 = bar
where
bar :: _
bar = show
在这种情况下,bar
不是通用的,也不是foo
(现在无点且没有签名)。这意味着单态类型变量永远不会被解析。根据单态限制的规则 2,它变成了一个模棱两可的类型变量:
<interactive>:718:14: error:
• Found type wildcard ‘_’ standing for ‘a0 -> String’
Where: ‘a0’ is an ambiguous type variable
To use the inferred type, enable PartialTypeSignatures
• In the type signature: bar :: _
In an equation for ‘foo9’:
foo9
= bar
where
bar :: _
bar = show
• Relevant bindings include
foo9 :: a0 -> String (bound at <interactive>:716:5)
<interactive>:719:13: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘show’
prevents the constraint ‘(Show a0)’ from being solved.
Relevant bindings include
bar :: a0 -> String (bound at <interactive>:719:7)
foo9 :: a0 -> String (bound at <interactive>:716:5)
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show a => Show (ZipList a)
-- Defined in ‘Control.Applicative’
instance Show Constr -- Defined in ‘Data.Data’
instance Show ConstrRep -- Defined in ‘Data.Data’
...plus 64 others
...plus 250 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the expression: show
In an equation for ‘bar’: bar = show
In an equation for ‘foo9’:
foo9
= bar
where
bar :: _
bar = show
bar
在定义中添加类型签名foo9
将无济于事——它只会改变报告错误的点。更改bar
为没有约束的东西确实消除了错误,因为它可以概括bar
和foo
。