Haskell TypeClasses-重写超类函数

Haskell TypeClasses-重写超类函数,haskell,typeclass,Haskell,Typeclass,考虑如下类型类: class A where fun1 :: String -- undefined function fun2 :: String -- function with default def using fun1 fun2 = fun1 我想定义不同的扩展功能的子类,并且能够为继承的fun1提供不同的定义。 例如: class A => B where fun3 :: String -- other functions for B

考虑如下类型类:

class A where
  fun1 :: String   -- undefined function     
  fun2 :: String   -- function with default def using fun1
  fun2 = fun1
我想定义不同的扩展功能的子类,并且能够为继承的fun1提供不同的定义。 例如:

class A => B where
  fun3 :: String   -- other functions for B
  fun1 = "B"       -- provide def for super class fun1 

class A => C where
  fun4 :: String   -- other functions for C
  fun1 = "C"       -- provide def for super class fun1
当我使用实例B时,我希望继承为B类定制的fun2功能。对于C

但是,编译器输出:

错误:“fun1”不是类“B”的可见方法

我知道类型类不是100%像普通的OOP。但是,不可能重写子类中的继承函数吗

我想定义不同的扩展功能的子类,并且能够为继承的fun1提供不同的定义

我暂且不谈为什么继承在那里不合适

如果您想为fun1“提供不同的定义”,那么需要在fun1声明中提供一些与差异相关的内容。换句话说,您需要一个类型参数:

呵呵,大脑信托基金会注意到:它进入深海的速度非常快,需要高级的典型狂犬病扩展。例如,在GHC7.10中不起作用

class A a  where
  fun1 :: a -> String
  fun2 :: String
  fun2 = fun1 (undefined :: a)
注意fun2::A=>String的派生类型。它仍然提到参数a,即使a既不是参数也不是结果。对于fun1::A=>A->String,A确实显示为参数,但当然,fun1的实例decls不需要实际检查其参数。fun2的默认定义将未定义的参数传递给fun1,所以它希望fun1不会检查它。它很可能是一个“幻影类型”,对类中的这个术语参数有很多解释。但我们不能确定,直到我们看到的例子

对于高级播放器:fun2还通过ScopedTypeVariables和类型注释::a传递类型参数a,也可以通过TypeApplications传递。需要AllowAmbigUstypes接受fun2的sig

现在我们可以为不同的类型重载fun1、fun2

instance A Int where
  fun1 _ = "Int"                  -- ignores its arg, so that's a phantom
  -- fun2                         -- not overloaded, so takes default  
回到你的生活

您希望如何访问fun1的此覆盖?没有机制,这就是编译器抱怨的原因。相反,您需要为fun1的一些重载提供一个钩子。可能是

newtype DB a = DB a
class A (DB a) => B a  where
  fun3 :: String                  -- again a is phantom
  fun3 = fun1 (undefined :: DB a)

instance B Int  where
  -- fun3 = fun1 (undefined :: DB Int)

instance A (DB Int)  where
  fun1 _ = "B"
回到继承的fun1:从类B到实例A的链接必须是通过某个类型,并带有fun1的重载。我使用了一种新类型来保持链接的轻量级。它不是OO意义上的继承机制

我知道类型类不是100%像普通的OOP

正如威廉评论的那样,这是一种轻描淡写的说法。Haskell类型类完全不同于OO类。简单地说,typeclass是一组类型,而OO类是一组对象。在Haskell中,我们称之为“对象”值,一组值大致是†类型所代表的,而不是类

换句话说,OO类更类似于单个Haskell类型,而不是Haskell类型的类

†同样,Haskell类型仅大致类似于一组值。有一些重要的区别,特别是Haskell不支持子类型,而OO子类确实代表了超类所代表的对象集的子集

如果你的例子,逆向工程成C++,则

class A {
 public:
  virtual String fun1() =0;
  String fun2() { return fun1(); }
};

class B: public A {
 public:
  virtual String fun3() =0;
  String fun1() { return "B"; }
};

class B: public A {
 public:
  virtual String fun4() =0;
  String fun1() { return "B"; }
};
那么对Haskell最直接的翻译是:

data A = A { str1 :: String }

class SubsumesA a where
  fun2 :: a -> String

instance SubsumesA A where
  fun2 = str1

data B = B { superB :: A
           , str3 :: String }

instance SubsumesA B where
  fun2 = const "B"

data C = C { superC :: A
           , str2 :: String }

instance SubsumesA C where
  fun2 = const "C"

但这通常不是编写Haskell类型层次结构的方式。Haskell不是一种OO语言,不要把它当作一种OO语言。具体地说,这种风格往往会让您陷入。

如果您的类型同时是B和C的成员该怎么办。Haskell中的类更像是Java/C中的接口。同样奇怪的是,您的类没有类型变量……您的类似乎没有类型参数。我很惊讶那些班长会编译。@AntC我同意这很奇怪,但我希望他们不会编译,因为我已经尝试过了。它们很难使用,至少在没有TypeApplications的情况下是如此,因为当你引用fun1时,你如何告诉编译器你想使用哪个实例呢?我知道类型类与普通OOP不同。。我认为这是一种轻描淡写的说法。Haskell的类型C++根本不像OO中的类。哦,好的C++。事实上,这或多或少就是我心目中的OOP设计。我以前从未使用过高级haskell特性,我也不使用haskell,但后来我在网上找到了一些文章,通过一些示例将类型类描述为最接近OOP的东西。因此,乍一看,类型类看起来更像接口,而不是实际的类。但类型类也允许默认定义,所以我认为可能可以从一些抽象类型类开始表示层次结构。我搞砸了,显然需要更多的学习。你对哈斯克尔的音译很有启发性。然而,在B和C实例中,我认为您分别指的是fun2=str3和fun2=str2。然后,fun2b=B,类似于示例C。在您的示例中,str1。出色的作品,如向上投射,以访问超级类功能
. 好的类比还是坏的类比?呵呵,你的第一个代码片段在GHC 8.6上对我不起作用,类型变量“a0”不明确。尝试了AllowAmbigUstypes,但未传递。需要其他扩展吗?fun2还通过ScopedTypeVariables传递类型参数a。另外,请养成报告哪一行/哪一段代码以及完整消息的习惯。我猜你无法从fun2的方程中使用fun1推导出a0:fun2=fun1未定义::A
data A = A { str1 :: String }

class SubsumesA a where
  fun2 :: a -> String

instance SubsumesA A where
  fun2 = str1

data B = B { superB :: A
           , str3 :: String }

instance SubsumesA B where
  fun2 = const "B"

data C = C { superC :: A
           , str2 :: String }

instance SubsumesA C where
  fun2 = const "C"