功能依赖关系中的不明确类型



在Haskell函数依赖维基中:

鉴于这些定义:

data Vector = Vector Int Int deriving (Eq, Show)
data Matrix = Matrix Vector Vector deriving (Eq, Show)
instance Num Vector where
  Vector a1 b1 + Vector a2 b2 = Vector (a1+a2) (b1+b2)
  Vector a1 b1 - Vector a2 b2 = Vector (a1-a2) (b1-b2)
  {- ... and so on ... -}
instance Num Matrix where
  Matrix a1 b1 + Matrix a2 b2 = Matrix (a1+a2) (b1+b2)
  Matrix a1 b1 - Matrix a2 b2 = Matrix (a1-a2) (b1-b2)
  {- ... and so on ... -}
class Mult a b c where
  (*) :: a -> b -> c
instance Mult Matrix Matrix Matrix where
  {- ... -}
instance Mult Matrix Vector Vector where
  {- ... -}

我不明白为什么有模棱两可的类型:

m1, m2, m3 :: Matrix
(m1 * m2) * m3              -- type error; type of (m1*m2) is ambiguous

显然,当 m1m2 是矩阵时,唯一可能的返回类型是矩阵,即应用 instance Mult Matrix Matrix Matrix

问题出在类型类声明上

class Mult a b c where
  (*) :: a -> b -> c

通过对两个参数应用(*),您无法确定结果的类型。假设您有两个实例:

instance Mult Int Int Int where ...
instance Mult Int Int Integer where ...

那么2 * 4可以是 Int 型,也可以是Integer型。

现在你可以争辩说你只有一个实例,所以编译器不应该抱怨。但是Haskell类型的类生活在一个开放的世界中。您始终可以添加更多实例,并且不得在其他地方破坏代码。因此,即使您只有一个实例,另一个库中的其他人也可以添加另一个实例。而且,您将有两个库,每个库都可以工作,但一起失败。这显然是不合时宜的。请参阅生活在现实世界中的开放世界中 哈斯克尔.

因此,通常类型类中函数的返回类型必须可从其参数派生。这正是功能依赖关系的用途。如果您声明

class Mult a b c | a b -> c where

然后编译器总是可以告诉(*)的返回类型是什么。

因为你忘记了所有其他的可能性,例如

 instance Mult Matrix Matrix Vector where
 instance Mult Vector Matrix Vector where
 instance Mult Float Matrix Float where
 instance Mult Matrix Matrix Float where  -- etc.
 a :: Vector
 a = (m1 >< m2:: Vector) >< m3
 b :: Float
 b = (m1 >< m2:: Float) >< m3

向类定义添加功能依赖项:

instance Mult a b c | a b -> c

将意味着

instance Mult Matrix Matrix Matrix

决定两个矩阵的问题,并且

instance Mult Matrix Matrix Vector
instance Mult Matrix Matrix Float

被排除在外,因为它们提供了另一种查看"MatrixMatrix的产品"的方式。因此,您发现直观的事态是您使用功能依赖性获得的状态。

如果功能依赖关系是这样制定的:

 instance Mult a b c | b -> a c

这也将允许您声明的两个实例,

 instance Mult Matrix Matrix Matrix where
 instance Mult Matrix Vector Vector where

但排除我想象的其他,它们都Matrix处于b位置(因为它们必须这样做,因为在模棱两可的例子中,m3被解释为Matrix而不是矩阵乘积的结果。

最新更新