假设我定义了 multi-parameter type class :
{-# LANGUAGE MultiParamTypeClasses, AllowAmbiguousTypes, FlexibleContexts, FlexibleInstances #-}
class Table a b c where
decrement :: a -> a
evalutate :: a -> b -> c
然后我定义一个使用
decrement
的函数,为简单起见:d = decrement
当我尝试在
ghci
中加载它时(版本 8.6.3):• Could not deduce (Table a b0 c0)
arising from a use of ‘decrement’
from the context: Table a b c
bound by the type signature for:
d :: forall a b c. Table a b c => a -> a
at Thing.hs:13:1-28
The type variables ‘b0’, ‘c0’ are ambiguous
Relevant bindings include d :: a -> a (bound at Thing.hs:14:1)
These potential instance exist:
instance Table (DummyTable a b) a b
这让我很困惑,因为
d
的类型正是 decrement
的类型,在类声明中表示。我想到了以下解决方法:
data Table a b = Table (a -> b) ((Table a b) -> (Table a b))
但这似乎在符号上不方便,而且我也只是想知道为什么我首先会收到此错误消息。
最佳答案
问题是,由于 decrement
只需要 a
类型,无法确定哪些类型 b
和 c
应该是,即使在调用函数的时候(从而将多态性解决为特定类型) - 因此,GHC 将无法决定使用哪个实例。
例如:假设您有两个 Table 实例:Table Int String Bool
, 和表 Int Bool Float
;你调用你的函数d
在应该将一个 Int 映射到另一个 Int 的上下文中 - 问题是,它匹配两个实例! ( a
两者都是 Int )。
注意,如果你让你的函数等于 evalutate
:
d = evalutate
然后编译器接受它。这是因为,由于
evalutate
取决于三个类型参数 a、b 和 c,调用站点的上下文将允许明确的实例解析 - 只需在调用它的位置检查 a、b 和 c 的类型。当然,对于单参数类型类来说,这通常不是问题——只有一种类型需要解决;当我们处理多个参数时,事情变得复杂......
一种常见的解决方案是使用 functional dependencies - 制作
b
和 c
取决于 a
: class Table a b c | a -> b c where
decrement :: a -> a
evalutate :: a -> b -> c
这告诉编译器,对于给定类型的每个 Table 实例
a
,只有一个实例(b
和 c
将由 a
唯一确定);所以它会知道不会有任何歧义并接受您的d = decrement
高兴地。
关于haskell - MultiParamTypeClasses - 为什么这个类型变量不明确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56487391/