混淆类型推断和函数依赖
Confusion with type inference and functional dependencies
我有以下多参数类型类,它对作为向量空间(向量)元素的类型具有函数依赖性
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
的实例,因此这样的实例不会与现有实例重叠,从而保留功能依赖性。
您定义了非常通用的实例:Vec v ...
。
没有重叠实例就不可能有其他实例。
例如添加
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
解决方案是定义辅助函数并编写实例定义"by hand":
{-# 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)
我有以下多参数类型类,它对作为向量空间(向量)元素的类型具有函数依赖性
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
的实例,因此这样的实例不会与现有实例重叠,从而保留功能依赖性。
您定义了非常通用的实例:Vec v ...
。
没有重叠实例就不可能有其他实例。
例如添加
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
解决方案是定义辅助函数并编写实例定义"by hand":
{-# 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)