未找到类型 class 实例,实例头包含未知类型变量

No type class instance was found, the instance head contains unknown type variables

好吧,只是尽可能简化:

有一个函数接受函子并做任何事情

sToInt :: ∀ a s. Functor s => s a -> Int
sToInt val = unsafeCoerce val

将此函数与函子一起使用 S 参数 (v) 也是函子。

-- declare date type S that is functor
data S (v :: Type -> Type) a = S (v a)

instance functorS :: Functor v => Functor (S v) where
    map f (S a) = S (map f a)

sV :: ∀ v a. S v a
sV = unsafeCoerce 1

sss :: Int
sss = sToInt sV -- get the error here

No type class instance was found for

    Data.Functor.Functor t2

  The instance head contains unknown type variables. Consider adding a type annotation.

while applying a function sToInt
  of type Functor t0 => t0 t1 -> Int
  to argument sV
while checking that expression sToInt sV
  has type Int
in value declaration sss

where t0 is an unknown type
      t1 is an unknown type
      t2 is an unknown type

所以它不喜欢 S Functor 实例有 v param Functor 约束,我想知道为什么会出现这个错误以及如何解决这个问题。

这与 vS 的具体形状无关。试试这个:

sV :: forall f a. Functor f => f a
sV = unsafeCoerce 1

sss :: Int
sss = sToInt sV

您遇到了类似的错误。

或者这里有一个更简化的版本:

sV :: forall a. a
sV = unsafeCoerce 1

sss :: Int
sss = sToInt sV

同样的错误。


问题是 sToInt 必须得到一个 Functor 实例作为参数(这就是它的类型签名中的 Functor s => 位所说的),并且为了选择哪个 Functor实例要传递,编译器需要知道值的类型。例如,如果它是 Maybe a,它将传递 Functor Maybe 实例,如果它是 Array a,它将传递 Functor Array 实例,依此类推。

通常可以从上下文推断类型。例如当你说 map show [1,2,3] 时,编译器知道 map 应该来自 Functor Array,因为 [1,2,3] :: Array Int.

但在你的情况下,没有地方可以获取该信息:sV 可以 return S v 任何 v,并且 sToInt 也可以任何函子类型。没有什么可以告诉编译器类型应该是什么。

解决这个问题的方法很明显:如果没有上下文信息供编译器从中获取类型,您必须自己告诉它类型是什么:

sss :: Int
sss = sToInt (sV :: S Maybe _)

这足以让编译器知道 v ~ Maybe,并且它将能够构造一个 Functor (S Maybe) 实例并将其传递给 sToInt


或者,如果您希望 sss 的消费者决定 v 是什么,您可以添加一个额外的虚拟参数来捕获类型,并要求消费者传入一个 Functor v实例:

sss :: forall v. Functor v => FProxy v -> Int
sss _ = sToInt (sV :: S v _)

ddd :: Int
ddd = sss (FProxy :: FProxy Maybe)

在 Haskell 中,您可以使用 visible type applications 而不是 FProxy 来执行此操作,但遗憾的是,PureScript 尚不支持。


或者,如果 sToInt 实际上不关心 Functor 实例,您可以从中删除该约束,一切都会按原样运行:

sToInt :: forall s a. s a -> Int
sToInt a = unsafeCoerce a

sV :: forall v a. S v a
sV = unsafeCoerce 1

sss :: Int
sss = sToInt sV

之所以可行,是因为 PureScript 允许存在不明确的(也称为“未知”)类型,只要它们不用于选择实例即可。