Haskell 多参数typeclass实例声明
我试图理解多参数类型类,但我没有得到实例声明。我开始尝试为向量类型创建一个InnerProductSpace类型类,这样我就可以对两个向量执行点积。首先,我只是想看看是否可以将每个向量的第一个元素相乘。这是我的密码Haskell 多参数typeclass实例声明,haskell,Haskell,我试图理解多参数类型类,但我没有得到实例声明。我开始尝试为向量类型创建一个InnerProductSpace类型类,这样我就可以对两个向量执行点积。首先,我只是想看看是否可以将每个向量的第一个元素相乘。这是我的密码 class InnerProductSpace a b c where dot :: a -> b -> c data Vector = Vector [Double] deriving (Show) instance InnerProductSpace Vect
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)
尝试使用dot函数后的错误是
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
我做错了什么?谢谢 问题在于,编译器不知道如何在已知的情况下选择正确的实例。如果你尝试像
vectorA `dot` vectorA
编译器开始搜索右边的dot
,知道它的类型必须是dot::Vector->Vector->c0
。不幸的是,这本身并没有足够的信息-c0
可以是任何东西,而且编译器从不认为仅仅因为它只有一个实例,它就必须是正确的实例(这与开放世界的假设有关——编译器可能还没有看到另一个实例,因此它更愿意抛出一个错误)。你可以通过明确地告诉编译器结果应该是什么来逃避它
vectorA `dot` vectorB :: Double
但是这很单调,而且可能会在使用数字类型时失败很多,因为这些类型通常也是泛型的
(vectorA `dot` vectorB) + 3
我们知道它是双精度的,但编译器无法证明这一点
一种解决方案是使用@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
或者使用TypeFamilies
class (D a b ~ c) => InnerProductSpace a b c where
type D a b
dot :: a -> b -> c
或
多参数类型类充满了概念上的障碍,而类型族在类型级别像函数一样工作,因此更容易。看看我对有类似问题的人的回答,以及我建议他们如何使用类型族来处理向量空间。谢谢!现在我将坚持使用函数依赖关系。这对我来说更有意义现在还没有。这是我和Haskell在一起的第一天,所以也许明天我会准备好处理类型族;-)难道不知道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)