混淆了类型推断和功能依赖

时间:2015-12-26 13:50:42

标签: haskell ghc

我有以下多参数类型类,它对作为向量空间(向量)元素的类型具有函数依赖性

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的实例,因此此类实例不会与现有实例重叠,从而保留功能依赖。

1 个答案:

答案 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)