我在一个非常简单的例子中遇到错误,我无法弄清楚出了什么问题。我正在做的与mempty
中的Monoid
非常相似,这是一个简单的版本(我包含我的量化类型,以防与问题有关):
data Field a = Field String
| forall b. EsMappable b => ReferenceField String (a -> b)
class EsMappable a where
fields :: [Field a]
toMapping :: (EsMappable a) => a -> String
toMapping a = go "" fields
where go str [] = str
go str (ReferenceField name _ : xs) = go (name ++ str) xs
go str (Field name : xs) =go (name ++ str) xs
我得到的错误是:
Could not deduce (EsMappable t0) arising from a use of ‘fields’
from the context (EsMappable a)
bound by the type signature for
toMapping :: EsMappable a => a -> String
at search2.hs:11:14-42
The type variable ‘t0’ is ambiguous
In the second argument of ‘go’, namely ‘fields’
In the expression: go "" fields
In an equation for ‘toMapping’:
toMapping a
= go "" fields
where
go str [] = str
go str (ReferenceField name _ : xs) = go (name ++ str) xs
go str (Field name : xs) = go (name ++ str) xs
注意:如果我将EsMapapble
类更改为:fields :: a -> [Field a]
,然后在toMapping
中更改go "" (fields a)
,则可以。
我的问题:为什么我收到此错误?这与mempty
不一样吗?什么阻止GHC正确解析fields
?
谢谢!
答案 0 :(得分:5)
问题是编译器无法将fields
的使用链接到您的参数a
。您的go
函数可以为任何 [Field a]
提供a
,因此您需要以某种方式明确约束它与a
相同在您的参数类型中。
您可以使用ScopedTypeVariables
:
toMapping :: forall a. (EsMappable a) => a -> String
toMapping _ = go "" (fields :: [Field a])
您需要额外的forall a
来明确地使a
类型变量作用域。为了向后兼容,这是对ScopedTypeVariables
的限制。
如果您将a
参数与Field a
中的go
值一起使用,那么这也不是问题。作为一个人为的例子,以下没有明确签名的类型检查:
go str (ReferenceField name f : xs) = go (name ++ str) xs `const` f a
这会强制f
采用a
类型的参数,这会将整个列表限制为特定的a
。所以你真的可以使用这个技巧来避免ScopedTypeVariables
扩展,如果你真的想要!我不建议这样做:扩展程序和它们来的一样无害,使代码更清晰。这个例子只是为了说明我的观点。