我有以下多参数类型类,它对作为向量空间(向量)元素的类型具有函数依赖性
module Vec where
class Vec v k | v -> k where -- v is an element of a vector space over k
vZero :: v -- The zero vector in v
vAdd :: v -> v -> v -- Adds two vectors
vSub :: v -> v -> v -- Subtracts two vectors
vMul :: v -> k -> v -- Multiplies a vector by a number from k
infixl 6 |+| -- Shortcut operator for accessing vAdd
(|+|) :: Vec v k => v -> v -> v
(|+|) = vAdd
现在我将上面的代码加载到ghci
解释器中,并要求它向我显示运算符|+|
的类型:
*Vec> :t (|+|)
(|+|) :: Vec v k => v -> v -> v
到现在为止,一切似乎都很正常。但现在我想指定所有数字都是特殊向量空间的元素:
instance Num k => Vec k k where
vZero = 0
vAdd = (+)
vSub = (-)
vMul = (*)
现在发生了一件奇怪的事情:ghci
不再显示|+|
的正确类型(尽管我在上面的代码中明确指出了它):
*Vec> :t (|+|)
(|+|) :: Num v => v -> v -> v
我怀疑这种奇怪的行为与我正在使用的FunctionalDependencies
语言扩展相关联,但我不明白为什么ghc
会这样做。我可以看到自己添加了一个不同的实例Vec v k
,其中v
不是 Num
的实例,因此此类实例不会与现有实例重叠,从而保留功能依赖。
答案 0 :(得分:4)
您已经定义了非常通用的实例:Vec v ...
。
如果没有重叠的实例,则不能有其他实例。
E.g。加入
data V2 k = V2 k k
instance Num k => Vec (V2 k) k where
结果
Functional dependencies conflict between instance declarations:
instance Num k => Vec k k -- Defined at v.hs:15:10
instance Num k => Vec (V2 k) k -- Defined at v.hs:23:10
实际上重叠的实例在这里也没有帮助(这可能是GHC未命中功能?)。
如果您尝试使用TypeFamilies
对此进行编码,则会出现类似错误:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Vec where
class Vec v where -- v is an element of a vector space over k
type Elem v
vZero :: v -- The zero vector in v
vAdd :: v -> v -> v -- Adds two vectors
vSub :: v -> v -> v -- Subtracts two vectors
vMul :: v -> Elem v -> v -- Multiplies a vector by a number from k
infixl 6 |+| -- Shortcut operator for accessing vAdd
(|+|) :: Vec v k => v -> v -> v
(|+|) = vAdd
data V2 k = V2 k k
instance Num k => Vec (V2 k) where
type Elem (V2 k) = k
-- implementation omitted
instance Num k => Vec k where
type Elem k = k
vZero = 0
vAdd = (+)
vSub = (-)
vMul = (*)
错误:
Conflicting family instance declarations:
Elem (V2 k) -- Defined at v.hs:20:10
Elem k -- Defined at v.hs:23:10
解决方案是定义辅助函数并编写实例定义"手工":
{-# LANGUAGE FlexibleInstances, FunctionalDependencies #-}
module Vec where
class Vec v k | v -> k where -- v is an element of a vector space over k
vZero :: v -- The zero vector in v
vAdd :: v -> v -> v -- Adds two vectors
vSub :: v -> v -> v -- Subtracts two vectors
vMul :: v -> k -> v -- Multiplies a vector by a number from k
infixl 6 |+| -- Shortcut operator for accessing vAdd
(|+|) :: Vec v k => v -> v -> v
(|+|) = vAdd
-- The definitions are so short, that it's not worth even givin them names
numVZero :: Num k => k
numVZero = 0
instance Vec Int Int where
vZero = 0
vAdd = (+)
vSub = (-)
vMul = (*)
data V2 k = V2 k k
instance Num k => Vec (V2 k) k where
vZero = V2 0 0
vAdd (V2 a b) (V2 c d) = V2 (a + c) (b + d)
vSub (V2 a b) (V2 c d) = V2 (a - c) (b - d)
vMul (V2 a b) k = V2 (a * k) (b * k)
然后:
λ *Vec > :t (|+|)
(|+|) :: Vec v k => v -> v -> v
或者Num v => Vec v v
可能很常见,您可以使用DefaultSignatures
减少实例声明的样板:
{-# LANGUAGE GADTs, FlexibleInstances, FunctionalDependencies, DefaultSignatures #-}
module Vec where
class Vec v k | v -> k where -- v is an element of a vector space over k
vZero :: v -- The zero vector in v
default vZero :: (Num v, v ~ k) => v
vZero = 0
vAdd :: v -> v -> v -- Adds two vectors
default vAdd :: (Num v, v ~ k) => v -> v -> v
vAdd = (+)
vSub :: v -> v -> v -- Subtracts two vectors
default vSub :: (Num v, v ~ k) => v -> v -> v
vSub = (-)
vMul :: v -> k -> v -- Multiplies a vector by a number from k
default vMul :: (Num v, v ~ k) => v -> k -> v
vMul = (*)
infixl 6 |+| -- Shortcut operator for accessing vAdd
(|+|) :: Vec v k => v -> v -> v
(|+|) = vAdd
instance Vec Int Int
instance Vec Integer Integer
instance Vec Float Float
instance Vec Double Double
data V2 k = V2 k k
instance Num k => Vec (V2 k) k where
vZero = V2 0 0
vAdd (V2 a b) (V2 c d) = V2 (a + c) (b + d)
vSub (V2 a b) (V2 c d) = V2 (a - c) (b - d)
vMul (V2 a b) k = V2 (a * k) (b * k)