6

假设我定义了一个多参数类型类

{-# 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

这让我感到困惑,因为 of 的类型d正是 的类型decrement,它在类声明中表示。

我想到了以下解决方法:

data Table a b = Table (a -> b) ((Table a b) -> (Table a b))

但这似乎在符号上不方便,而且我也只是想知道为什么我首先会收到此错误消息。

4

1 回答 1

7

问题是,由于decrement只需要类型,因此即使在调用函数的点(从而将多态性解决为特定类型),也无法a确定哪些类型应该是 - 因此,GHC 将无法决定使用哪个实例。bc

例如:假设您有两个 Table:Table Int String Bool和 Table实例Int Bool Floatd您在应该将 Int 映射到另一个 Int 的上下文中调用函数- 问题是,它匹配两个实例!(a两者都是 Int )。

请注意,如果您使函数等于evalutate

d = evalutate

然后编译器接受它。这是因为,由于evalutate取决于三个类型参数 a、b 和 c,调用站点的上下文将允许明确的实例解析 - 只需检查 a、b 和 c 的类型它被称为。

当然,对于单参数类型类来说,这通常不是问题——只有一种类型需要解决;当我们处理多个参数时,事情变得复杂......

一种常见的解决方案是使用功能依赖项- makebc依赖于a

 class Table a b c | a -> b c where
  decrement :: a -> a
  evalutate :: a -> b -> c

这告诉编译器,对于给定类型的每个 Table 实例a,将有一个,并且只有一个实例(b并且c将由 唯一确定a);所以它会知道不会有任何歧义并d = decrement愉快地接受你。

于 2019-06-07T03:15:55.693 回答