我正在使用Haskell的FunctionalDependencies-Extension以及MultiParamTypeClasses。我定义了以下内容:
class Add a b c | a b -> c where
(~+) :: a -> b -> c
(~-) :: a -> b -> c
neg :: a -> a
zero :: a
工作正常(我尝试过Int和Double的实例,最终目标是能够在没有显式转换的情况下添加Int和Doubles)。
当我尝试定义neg或(〜 - )的默认实现时,如:
class Add ...
...
neg n = zero ~- n
GHCi(7.0.4)告诉我以下内容:
Ambiguous type variables `a0', `b0', `c0' in the constraint:
(Add a0 b0 c0) arising from a use of `zero'
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `(~-)', namely `zero'
In the expression: zero ~- n
In an equation for `neg': neg n = zero ~- n
Ambiguous type variable `a0' in the constraint:
(Add a0 a a) arising from a use of `~-'
Probable fix: add a type signature that fixes these type variable(s)
In the expression: zero ~- n
In an equation for `neg': neg n = zero ~- n
我想我确实理解这里的问题。 GHC不知道使用哪个零,因为它可以是任何零,而是产生任何反过来被输入~-
的东西,我们只知道它有一个{{1}在它的正确参数中产生a
。
那么如何指定它应该是来自同一个实例的零,即如何表达如下内容:
a
我认为neg n = (zero :: Add a b c) ~- n
,a
和b
这里不 abc形成了周围的类,但是任何ab和c,所以我怎么能表达一个类型,它是对本地类型变量的引用?
答案 0 :(得分:6)
将neg
和zero
拉出到仅使用一种类型的超类中:
class Zero a where
neg :: a -> a
zero :: a
class Zero a => Add a b c | a b -> c where
(~+) :: a -> b -> c
(~-) :: a -> b -> c
重点是,zero :: Int
可以是来自zero
的{{1}},或来自Add Int Int Int
的{{1}} ,无论你是从默认实现,实例声明还是普通代码中引用它,都无法消除两者之间的歧义。
(您可能反对来自zero
的{{1}}和来自Add Int Double Double
的{{1}}具有相同的值,但编译器无法知道某人不是'我将在另一个模块中定义zero
,并在那里给Add Int Int Int
一个不同的值。)
通过将类型类拆分为两个,我们消除了歧义。
答案 1 :(得分:3)
您无法将zero
函数表示为Add
类的一部分。必须在类的每个函数的类型声明中遇到类声明中的所有类型变量;否则,Haskell将无法决定使用哪种类型的实例,因为它的约束条件太少了。
换句话说,zero
不是您正在建模的类的属性。您基本上是在说:“对于任何三种类型a
,b
,c
,类型a
”必须存在零值,这没有任何意义;您可以选择任何b
和c
来解决问题,因此b
和c
完全无法使用,因此如果您有Add Int Int Int
或Add Int (Maybe String) Boat
,Haskell不知道更喜欢哪个实例。您需要将否定属性和“zeroness”分隔为单独的类别:
class Invertible a where
invert :: a -> a
neg :: Invertible a => a -> a
neg = invert
class Zero a where
zero :: a
class Add a b c | a b -> c where
(~+) :: a -> b -> c
(~-) :: a -> b -> c
我不明白为什么你甚至需要Invertible
中的Zero
和Add
约束;你可以随时添加数字而不知道它们的零值,不是吗?为什么要neg
作为~+
的要求;有一些数字应该是可添加的,而不是它们可以否定(例如自然数)?只需将课堂问题分开。