加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 为什么喜欢Typeclass继承?

发布时间:2020-12-16 19:04:37 所属栏目:安全 来源:网络整理
导读:根据这个 Erik Osheim’s slide,他说继承可以解决类似的问题, 但是提到继承有一个问题叫做: brittle inheritance nightmare 并说继承是 tightly coupling the polymorphism to the member types 他是什么意思? 在我看来,继承是扩展的好处,要么改变现有类型
根据这个 Erik Osheim’s slide,他说继承可以解决类似的问题,
但是提到继承有一个问题叫做:

brittle inheritance nightmare

并说继承是

tightly coupling the polymorphism to the member types

他是什么意思?

在我看来,继承是扩展的好处,要么改变现有类型的实现,要么添加新的成员类型到接口.

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)

现在假设我们要实现加法

>两个实数
>一个真实和复杂的数字
>两个复数

使用继承的解决方案(编辑:实际上,我不知道这是否可以称为继承,因为方法添加在traits中没有实现.但是,在这方面,这个例子与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的实现与Real和Complex紧密耦合,因此每次添加新类型(例如,整数)和每次需要新的操作(例如,减法)时,我们必须扩大它们的接口.

类型类提供了一种将实现与类型分离的方法.例如,我们可以定义特征

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

并单独实施使用隐含的添加

object Implicits {
  def add[A,C](a: A,b: B)(implicit ev: CanAdd[A,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,b: Real): Complex = Complex(a.real + b.value,a.imaginary)
  }
  implicit object CanAddRealComplex extends CanAdd[Real,Complex] {
    override def add(a: Real,b: Complex): Complex = Complex(a.value + b.real,b.imaginary)
  }
}

这种解耦至少有两个好处

>防止真实和复杂的接口污染
>允许引入新的CanAdd功能,无需修改可以添加的类的源代码

例如,我们可以定义CanAdd [Int,Int,Int]添加两个Int值而不修改Int类:

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读