簡體   English   中英

為什么更喜歡Typeclass而不是繼承呢?

[英]Why prefer Typeclass over Inheritance?

根據Erik Osheim的幻燈片 ,他說繼承可以解決與類型類相同的問題,但提到繼承有一個問題叫做:

脆弱的遺產噩夢

並說繼承是

將多態性與成員類型緊密耦合

他的意思是什么?


在我看來,繼承很擅長擴展,要么改變現有類型的實現,要么添加新的成員類型(子類型)到接口。

trait Foo { def foo }

class A1 extends Foo{
  override def foo: Unit = ???
}

//change the foo implementation of the existing A1
class A2 extends A1 with Foo{  
  override def foo = ???
}

// add new type B1 to Fooable family
class Bb extends Foo{        
  override def foo = ???
}

現在就類型類而言:

trait Fooable[T] { … }
def foo[T:Fooable](t:T) = …

class Aa {…}
class Bb {…}
object MyFooable {
  implicit object AaIsFooable extends Fooable[Aa]
  implicit object B1IsFooable extends Fooable[Bb]
   …
}

我沒有看到任何理由更喜歡Typeclass,我錯過了什么?

當使用繼承來實現ad-hoc多態時,我們可能需要嚴重污染我們的值對象的接口。

假設我們想要實現一個實數和一個復數。 沒有任何功能,這就像寫作一樣簡單

case class Real(value: Double)

case class Complex(real: Double, imaginary: Double)

現在假設我們要實現添加

  • 兩個實數
  • 一個真實和復雜的數字
  • 兩個復數

使用繼承的解決方案( 編輯:實際上,我不確定這是否可以稱為繼承,因為方法中add的特征沒有實現。但是,在這方面,示例與Erik Orheim的示例沒有區別)看起來像這個:

trait AddableWithReal[A] {
  def add(other: Real): A
}

trait AddableWithComplex[A] {
  def add(other: Complex): A
}

case class Real(value: Double) extends AddableWithComplex[Complex] with AddableWithReal[Real] {
  override def add(other: Complex): Complex = Complex(value + other.real, other.imaginary)

  override def add(other: Real): Real = Real(value + other.value)
}

case class Complex(real: Double, imaginary: Double) extends AddableWithComplex[Complex] with AddableWithReal[Complex] {
  override def add(other: Complex): Complex = Complex(real + other.real, imaginary + other.imaginary)

  override def add(other: Real): Complex = Complex(other.value + real, imaginary)
}

因為add的實現與RealComplex緊密耦合,所以每次添加新類型(例如,整數)並且每次需要新操作(例如,減法)時,我們必須擴大它們的接口。

類型類提供了一種將實現與類型分離的方法。 例如,我們可以定義特征

trait CanAdd[A, B, C] {
  def add(a: A, b: B): C
}

並使用implicits單獨實現添加

object Implicits {
  def add[A, B, C](a: A, b: B)(implicit ev: CanAdd[A, B, C]): C = ev.add(a, b)
  implicit object CanAddRealReal extends CanAdd[Real, Real, Real] {
    override def add(a: Real, b: Real): Real = Real(a.value + b.value)
  }
  implicit object CanAddComplexComplex extends CanAdd[Complex, Complex, Complex] {
    override def add(a: Complex, b: Complex): Complex = Complex(a.real + b.real, a.imaginary + b.imaginary)
  }
  implicit object CanAddComplexReal extends CanAdd[Complex, Real, Complex] {
    override def add(a: Complex, b: Real): Complex = Complex(a.real + b.value, a.imaginary)
  }
  implicit object CanAddRealComplex extends CanAdd[Real, Complex, Complex] {
    override def add(a: Real, b: Complex): Complex = Complex(a.value + b.real, b.imaginary)
  }
}

這種脫鈎至少有兩個好處

  1. 防止RealComplex界面的污染
  2. 允許引入新的CanAdd功能,但無法修改可添加的類的源代碼

例如,我們可以定義CanAdd[Int, Int, Int]來添加兩個Int值而無需修改Int類:

implicit object CanAddIntInt extends CanAdd[Int, Int, Int] {
  override def add(a: Int, b: Int): Int = a + b
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM