初步说明:鉴于此处提供的证据,我假设您正在使用:
-
type GenericQ r = forall a . Data a => a -> rfrom syb,和
-
gmapQ :: Data a => (forall d. Data d => d -> u) -> a -> [u]from Data.Data.
如果我对此有误,请告诉我。此外,下文中的任何foralls 都将被明确写入。
这里有很多东西。 As Li-yao Xia suggests,这是涉及op类型的泛化问题。关于您对shallowest 的第一个定义,有三个相关事实:
在泛化之前,op 的推断类型是Data d => d -> f a。给定Data d约束,单态约束规则1(见subsection 4.5.5 of the Report)意味着这种类型的d不能泛化。
在shallowest 的正文中,op 出现在两个地方。第一个是op z,z :: a1 在顶层被shallowest 的签名约束和约束。结果是op 的出现不需要参数类型的泛化:就它而言,op 的类型可以是forall f a. a1 -> f a,在类型变量中是单态的a1(这个术语来自subsection 4.5.4 of the Report)。
不过,另一种情况是gmapQ op z。 gmapQ 具有 rank-2 类型,需要多态参数。既然如此,这种情况需要泛化 op 的参数类型,正如夏立尧回答末尾所指出的那样。
#1 和 #3 是相互矛盾的要求,因此您会遇到类型错误,可以通过禁用单态限制或要求 op 在带有签名的参数类型上具有多态性来避免这种错误。由于在 #2 中描述了 op 的另一次出现,这种情况被报告为涉及这两次出现的不匹配。
下面是一个更简单的扩展示例,它可能有助于了解发生了什么。 (如果你要将以下 sn-ps 放入 GHCi,除了-XRankNTypes,你还应该设置-XMonomorphismRestriction 和-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 的定义变得有意义也是如此,它通过使其成为a "function binding" rather than a "simple pattern binding" 来规避单态性限制:
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
在foo9 的定义中将类型签名添加到bar 将无济于事——它只会更改报告错误的起点。将bar 更改为没有约束的东西确实消除了错误,因为它可以概括bar 和foo。