不能从上下文中推断(简单)类型类

aar*_*vin 3 haskell

我在一个非常简单的例子中得到一个错误,我无法弄清楚出了什么问题.我正在做的非常类似于memptyin 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
Run Code Online (Sandbox Code Playgroud)

我得到的错误是:

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
Run Code Online (Sandbox Code Playgroud)

注意:如果我将EsMapapble类更改为:fields :: a -> [Field a]然后在toMapping我更改时go "" (fields a),它可以工作.

我的问题:为什么我会收到此错误?这不一样mempty吗?什么阻止GHC正确解决fields

谢谢!

Tik*_*vis 5

问题是编译器无法将您的使用链接fields到您的参数a.你的go函数可以在[Field a]任何 a,所以你需要以某种方式明确限制它是一样的a在你的参数类型.

你可以很好地做到这一点ScopedTypeVariables:

toMapping :: forall a. (EsMappable a) => a -> String
toMapping _ = go "" (fields :: [Field a])
Run Code Online (Sandbox Code Playgroud)

你需要额外的forall a来明确地使a类型变量作用域.这是为了ScopedTypeVariables向后兼容性的限制.

如果您将a参数与Field a值一起使用,这也不会成为问题go.作为一个人为的例子,以下没有明确签名的类型检查:

go str (ReferenceField name f : xs) = go (name ++ str) xs `const` f a
Run Code Online (Sandbox Code Playgroud)

这会强制f采用类型的参数a,这会将整个列表限制为特定的a.所以你真的可以使用这个技巧来避免ScopedTypeVariables扩展,如果你真的想!我不建议这样做:扩展程序和它们来的一样无害,使代码更清晰.这个例子只是为了说明我的观点.