Haskell 用新成员重写类实例

Haskell 用新成员重写类实例,haskell,typeclass,Haskell,Typeclass,假设我有一个简单的类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 mem

假设我有一个简单的类
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
包装替换
Int
),但上述代码中唯一不可变的是
AClass
的类声明。这是因为假设这个类已经被一个图书馆的作者写过了,所以我不能碰它。最终结果应该是另一个玩具代码,它继承了
AClass
的优点,并添加了
f2
成员

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

--更新--

工作代码如下-感谢Ben和mergeconflict提出的解决方案-缺少的部分很少-填写如下:

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                       

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


您只需定义一个函数f2=0.0。除非将其添加到定义中,否则它不会成为typeclass AClass的成员——但为什么需要它呢?

您想实现什么

您有一个类型
Val
,它是
AClass
的一个实例。您可以定义任意数量的使用
Val
且与类无关的函数。停止在
实例
声明中定义它们

如果您期望的是能够拥有一个具有额外的
f2
函数的
AClass
实例,那么您可以在使用
AClass
实例的函数中使用该函数,并让它们能够调用
f2
。。。那太荒谬了。根据定义,所有
AClass
实例所共有的唯一已知内容是
AClass
中声明的内容。如果您只知道某个值是属于
AClass
实例类型的成员,那么您不能对该值执行任何不能对
AClass
的所有实例执行的操作。对于某些特定的实例,您不能调用任何额外的内容

如果要创建一个新类,该类支持
AClass
以及
f2
所执行的所有操作,并使
Val
成为该新类的实例。。。那你就这么做吧

class AClass a => AnotherClass a where
    f2 :: a -> Float

instance AnotherClass Val where
    f2 x = 0.0

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

假设我有一个简单的类
AClass
,其中有一个公共成员
f1
,该成员可以被覆盖

如果您考虑的是具有可以“重写”的“公共成员”的“类”,那么您考虑的是面向对象的术语。您展示的代码根本不代表这些概念。见托尼·莫里斯的帖子

类型类定义了一个概念(在中,如果有帮助的话)。该概念由一组功能组成,例如:

class Eq a where 
  (==) :: a -> a -> Bool
class (Eq a) => Num a where
  (+), (-), (*) :: a -> a -> a
。。。但是没有这些功能的实际行为或实现。这里没有什么可以“重写”的。为该概念建模的数据类型将提供实例声明,如:

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 where 
  (==) :: a -> a -> Bool
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中有什么可能,以及如何实现。是的,一个支持AClass操作并执行f2的类将如何工作。但是,如果我尝试你的方法,我会得到一个错误,f1不是另一个类的可见成员。f1和f2的实现都留待实例处理,这就是错误的来源。
类AClass a=>另一个类a,其中…
表示类型
a
必须是
AClass
的实例,才能成为
另一个类的实例。如果您没有做出这样的声明,那将是您的错误。@sal您真的打算用不同的
f2
实现另一个类的多个实例吗?如果它只是
Val
,那么直接在
Val
上实现
f2
并使用
Val
而不是
AClass
键入其他代码会简单得多。如果已知
Val
是它的一个实例,您仍然可以使用
AClass
功能。@Ben,我计划有几个实例声明,每个声明都提供各自的f1和f2实现。很好地了解了OP的OOthinking@mergeconflict是的,用你的话来说,我想,“模型比以前定义的类型类更具体的概念”。您发布的最后一段代码与解决此问题有关(与Ben发布的代码类似)。问题是我无法在Val的实例声明中实现f1,因为编译器抱怨f1不是BClass的可见成员。那么,我们如何让实例声明实现f