用新成员重写类实例



假设我有一个简单的类AClass,它有一个可以被覆盖的公共成员f1。在不复制AClass的源代码的情况下,如何用另一个成员f2定义AClass的新实例?下面的玩具代码:

class AClass a where
    f1 :: a -> Int
data Val = I Int
instance AClass Val where
  f1 x = 0
  -- the method below can't be added as it is not public member of AClass
  -- f2:: a -> Float
  -- f2 x = 0.0 

我环顾四周,但我没有找到任何关于如何做的清晰的例子(即,我能很好地理解的例子-清晰是相对的)。有哪些可能的方法?闭包,newtype声明还是别的什么?用上面的玩具代码来演示这个技术会很有帮助——你可以改变data的声明等(例如,用newtype包装器替换Int),但上面代码中唯一不变的是AClass的类声明。这是因为假设类已经由库编写者编写,所以我不能碰它。最终的结果应该是另一个玩具代码继承AClass的优点,并增加f2成员。

在重写这样的类时,当然会有一些警告。但是,它有助于了解什么是可能的,以及如何。

—Update—

下面的工作代码-感谢Ben和mergconflict提出的解决方案-几乎没有遗漏的部分-填写在下面:

class AClass a where
    f1 :: a -> Int
class (AClass a) => BClass a where
    f2 :: a -> Float
data Val = I Int
instance AClass Val where
   f1 _ = 0
instance BClass Val where
   f2 _ = 0.0                       

你想达到什么目标?

您有一个类型Val,您将其作为AClass的实例。您可以定义任意数量的使用Val的函数,这些函数与类无关。只要停止尝试在instance声明中定义它们。

如果你期望的是能够有一个特定的AClass实例,它有一个额外的f2函数,然后你在使用AClass实例的函数中使用它,并让它们能够调用f2…这是荒谬的。根据定义,所有AClass实例已知的唯一共同的东西是在AClass中声明的东西。如果你只知道某个值是AClass实例类型的成员,你就不能对它做任何你不能对所有AClass实例做的事情。你不能调用任何特定于特定实例的额外内容。

如果你想创建一个新的类,它支持AClassf2的所有操作,并且让Val成为这个新类的一个实例…那你就照做吧
class AClass a => AnotherClass a where
    f2 :: a -> Float
instance AnotherClass Val where
    f2 x = 0.0

你的问题在Haskell中没有意义:

假设我有一个简单的类AClass,它有一个可以被重写的公共成员f1

如果您正在考虑具有可以"重写"的"公共成员"的"类",那么您正在考虑面向对象的术语。您所展示的代码根本没有表示这些概念。参见Tony Morris的文章"类型类与接口完全不同"。

一个类型类定义了一个概念(在c++的意义上,如果有帮助的话)。该概念由一组函数组成,例如:

class Eq a where 
  (==) :: a -> a -> Bool

…但是没有这些功能的实际行为或实现。这里没有什么可以"推翻"的。模型这个概念的数据类型将提供实例声明,如:

data Integer = {- ... -}
instance Eq Integer where 
  x == y =  x `integerEq` y
data Float = {- ... -}
instance Eq Float where
  x == y =  x `floatEq` y

所以你可以实现多态算法,如:

allEqual :: Eq a => a -> a -> a -> Bool
allEqual a b c = (a == b) && (b == c)
现在,回到您的问题,您还可以定义类型类,这些类型类比以前定义的类型类建模更具体的概念。例如:
class (Eq a) => Num a where
  (+), (-), (*) :: a -> a -> a

因此,存在Eq的实例不是Num的实例,但所有Num的实例必须是Eq的实例。在您的示例中,您可能需要这样的内容:

class AClass a where
  f1 :: a -> Int
class (AClass b) => BClass b where
  f2 :: a -> Float
data Val = {- whatever -}
instance BClass Val where
  f1 _ = 0
  f2 _ = 0.0

同样,Val本身并不是"继承了好东西",它只是说它是BClass的一个实例,因此也是AClass的一个实例。但这显然是玩具代码…

你应该记住Haskell不是面向对象的,Haskell类型类也不太像面向对象意义上的类。

你可以简单地定义一个函数f2 _ = 0.0。除非将它添加到定义中,否则它不会是类型类AClass的成员——但是为什么需要它呢?

最新更新