MultiParamTypeClasses - 为什么这个类型变量不明确?
MultiParamTypeClasses - Why is this type variable ambiguous?
假设我定义了一个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
的类型,这在 class 声明中表示。
我想到了以下解决方法:
data Table a b = Table (a -> b) ((Table a b) -> (Table a b))
但这在符号上似乎很不方便,我也只是想知道为什么我一开始会收到这条错误消息。
问题在于,由于decrement
只需要a
类型,所以无法确定b
和c
应该是哪种类型,甚至在调用函数的地方(从而将多态性解决为特定类型)——因此,GHC 将无法决定使用哪个实例。
例如:假设您有两个 Table 实例:Table Int String Bool
和 Table Int Bool Float
;您在应该将一个 Int 映射到另一个 Int 的上下文中调用您的函数 d
- 问题是,它匹配 both 个实例! (a
两者都是 Int)。
请注意,如果您使函数等于 evalutate
:
d = evalutate
然后编译器接受它。这是因为,由于 evalutate
取决于三个类型参数 a、b 和 c,调用站点的上下文将允许 non-ambiguous 实例解析 - 只需检查 a、b 的类型, 和 c 在它被调用的地方。
当然,对于 single-parameter 类型 类 这通常不是问题 - 只有一种类型需要解决;当我们处理多个参数时,事情就变得复杂了...
一个常见的解决方案是使用 functional dependencies - 使 b
和 c
依赖于 a
:
class Table a b c | a -> b c where
decrement :: a -> a
evalutate :: a -> b -> c
这告诉编译器,对于给定类型 a
的每个 Table 实例,将有一个且只有一个实例(b
和 c
将由 a
) 唯一确定);所以它会知道不会有任何歧义并愉快地接受你的d = decrement
。
假设我定义了一个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
的类型,这在 class 声明中表示。
我想到了以下解决方法:
data Table a b = Table (a -> b) ((Table a b) -> (Table a b))
但这在符号上似乎很不方便,我也只是想知道为什么我一开始会收到这条错误消息。
问题在于,由于decrement
只需要a
类型,所以无法确定b
和c
应该是哪种类型,甚至在调用函数的地方(从而将多态性解决为特定类型)——因此,GHC 将无法决定使用哪个实例。
例如:假设您有两个 Table 实例:Table Int String Bool
和 Table Int Bool Float
;您在应该将一个 Int 映射到另一个 Int 的上下文中调用您的函数 d
- 问题是,它匹配 both 个实例! (a
两者都是 Int)。
请注意,如果您使函数等于 evalutate
:
d = evalutate
然后编译器接受它。这是因为,由于 evalutate
取决于三个类型参数 a、b 和 c,调用站点的上下文将允许 non-ambiguous 实例解析 - 只需检查 a、b 的类型, 和 c 在它被调用的地方。
当然,对于 single-parameter 类型 类 这通常不是问题 - 只有一种类型需要解决;当我们处理多个参数时,事情就变得复杂了...
一个常见的解决方案是使用 functional dependencies - 使 b
和 c
依赖于 a
:
class Table a b c | a -> b c where
decrement :: a -> a
evalutate :: a -> b -> c
这告诉编译器,对于给定类型 a
的每个 Table 实例,将有一个且只有一个实例(b
和 c
将由 a
) 唯一确定);所以它会知道不会有任何歧义并愉快地接受你的d = decrement
。