我试图理解多参数类型类,但我只是没有得到实例声明。我开始尝试为Vector类型创建一个InnerProductSpace类型类,以便我可以在两个向量上执行点积。首先,我想知道是否可以将每个向量的第一个元素相乘。这是我的代码
class InnerProductSpace a b c where
dot :: a -> b -> c
data Vector = Vector [Double]
deriving (Show)
instance InnerProductSpace Vector Vector Double where
dot (Vector a) (Vector b) = (head a * head b)
尝试使用点功能后的错误是
No instance for (InnerProductSpace Vector Vector c0)
arising from a use of `dot'
The type variable `c0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there is a potential instance available:
instance InnerProductSpace Vector Vector Double
-- Defined at Vector.hs:8:10
Possible fix:
add an instance declaration for
(InnerProductSpace Vector Vector c0)
In the expression: dot a b
In an equation for `it': it = dot a b
我做错了什么?谢谢!
答案 0 :(得分:7)
问题是编译器不知道如何根据它知道的选择正确的实例。如果您尝试类似
的内容vectorA `dot` vectorA
编译器会搜索权限dot
,知道其类型必须为dot :: Vector -> Vector -> c0
。不幸的是,这本身就没有足够的信息 - c0
可能是任何东西而且编译器从不假设只是因为它只有一个实例它必须是正确的一个(这与开放世界的假设--- 可能是编译器还没有看到的另一个实例,所以它更喜欢抛出错误)。你可以通过明确地告诉编译器结果应该是什么来逃避它
vectorA `dot` vectorB :: Double
但这很繁琐,可能会因为数字类型而失败很多,因为通常这些类型也是通用的。例如,这里使用了Num
的哪个实例?
(vectorA `dot` vectorB) + 3
我们知道它是Double
,但编译器不能证明。
一种解决方案是使用Type Families作为@AndrewC在评论中建议。我实际上强烈推荐。您将在野外看到的另一个解决方案是功能依赖项。这些都是这样写的
class InnerProductSpace a b c | a b -> c where
dot :: a -> b -> c
并翻译为需要对编译器的承诺:“知道a
和b
是足以唯一标识c
的信息”。编译器会保证您的诚实,并且它会阻止您编写与该承诺冲突的实例。
instance InnerProductSpace Vector Vector Double where
dot (Vector a) (Vector b) = (head a * head b)
instance InnerProductSpace Vector Vector Float where
dot (Vector a) (Vector b) = (head a * head b)
---
/Users/tel/tmp/foo.hs:10:10:
Functional dependencies conflict between instance declarations:
instance InnerProductSpace Vector Vector Double
-- Defined at /Users/tel/tmp/foo.hs:10:10
instance InnerProductSpace Vector Vector Float
-- Defined at /Users/tel/tmp/foo.hs:13:10
但是promise给了编译器足够的信息来解析前一个例子中的Double
。
Main*> (Vector [1,2,3]) `dot` (Vector [2,3,4]) + 3.0
5
答案 1 :(得分:3)
或使用TypeFamilies
class (D a b ~ c) => InnerProductSpace a b c where
type D a b
dot :: a -> b -> c
或
class InnerProductSpace a b where
type D a b :: *
dot :: a -> b -> D a b
instance InnerProductSpace Vector Vector where
type D Vector Vector = Double
dot (Vector a) (Vector b) = (head a * head b)