Scala类型的多态性

时间:2013-12-05 23:30:32

标签: oop scala functional-programming polymorphism typeclass

我们正在重构继承的method以使用类型类 - 我们希望将所有method实现集中在一个地方,因为将它们分散在实现类中会使维护变得困难。但是,我们遇到了一些麻烦,因为我们对类型类很新。目前method定义为

trait MethodTrait {
  def method: Map[String, Any] = // default implementation
}

abstract class SuperClass extends MethodTrait {
  override def method = super.method ++ // SuperClass implementation
}

class Clazz extends SuperClass {
  override def method = super.method ++ // Clazz implementation
}

等等,总共有50多个具体类,层次结构相当浅(abstract class SuperClass - > abstract class SubSuperClass - > abstract class SubSubSuperClass - > {{ 1}}就像它一样深,并且具体的类永远不会扩展另一个具体的类。 (在实际实施中,class ConcreteClass会返回Play框架method而不是JsObject。)我们尝试将其替换为类型类:

Map[String, Any]

我遇到两个问题:

A 即可。模仿先前实现中的trait MethodTrait[T] { def method(target: T): Map[String, Any] } class MethodType { type M[T] = MethodTrait[T] } implicit object Clazz1Method extends MethodTrait[Clazz1] { def method(target: Clazz1): Map[String, Any] { ... } } implicit object Clazz2Method extends MethodTrait[Clazz2] { def method(target: Clazz2): Map[String, Any] { ... } } // and so on 功能。目前我正在使用

super.method ++

但是这很难看,如果我不小心将方法调用到层次结构太远,我就不会收到警告或错误,例如如果class Clazz1 extends SuperClass class Clazz2 extends SubSuperClass private def superClassMethod(s: SuperClass): Map[String, Any] = { ... } private def subSuperClassMethod(s: SubSuperClass): Map[String, Any] = { superClassMethod(s) ++ ... } implicit object Clazz1Method extends MethodTrait[Clazz1] { def method(target: Clazz1): Map[String, Any] = { superClassMethod(target) ++ ... } } implicit object Clazz2Method extends MethodTrait[Clazz2] { def method(target: Clazz2): Map[String, Any] = { subSuperClassMethod(target) ++ ... } } 拨打Clazz2而不是superClassMethod

即可。在超类上调用subSuperClassMethod,例如

method

理想情况下,我希望能够告诉编译器val s: SuperClass = new Clazz1() s.method 的每个子类在类型类中都有SuperClass的对应隐式对象,因此method是类型安全的(或者,如果我忽略了为s.method的子类实现相应的隐式对象,我将得到编译时错误,但是我已经能够提出的是

SuperClass

这是丑陋的,如果我省略了一个类,就不会给我编译时警告或错误,因为我无法将implicit object SuperClassMethod extends MethodTrait[SuperClass] { def method(target: SuperClass): Map[String, Any] = { target match { case c: Clazz1 => c.method case c: Clazz2 => c.method ... } } } 定义为密封特征。


我们愿意接受类型类的替代方法,这样我们就可以将SuperClass代码集中在一个地方。 method仅在两个地方被调用:

一个。其他method实施,例如methodClazz1,在这种情况下,val clazz2: Option[Clazz2]中的method实施就像

Clazz1

B中。顶级Play Framework控制器(即所有控制器继承的抽象类),我们在其中定义了一个调用def method = super.method ++ /* Clazz1 method implementation */ ++ clazz2.map(_.method).getOrElse(Map()) 的三个ActionBuilders,例如

method

3 个答案:

答案 0 :(得分:6)

我认为类型类与您的场景不兼容。当类型不相交时,它们很有用,但实际上要求实例反映超类型/子类型层次结构并且不是独立的。

通过这种重构,您只是创造了挑选错误实例的危险:

trait Foo
case class Bar() extends Foo

trait HasBaz[A] { def baz: Set[Any] }

implicit object FooHasBaz extends HasBaz[Foo] { def baz = Set("foo") }
implicit object BarHasBaz extends HasBaz[Bar] { def baz = FooHasBaz.baz + "bar" }

def test[A <: Foo](x: A)(implicit hb: HasBaz[A]): Set[Any] = hb.baz

val bar: Foo = Bar()
test(bar) // boom!

所以你最终用SuperClassMethod中的模式匹配器重写了多态分派。你基本上去OO - &gt; FP - &gt; OO,同时使类型类的想法无法使用(待打开),而是以和类型结束(已知所有子类型)。

答案 1 :(得分:2)

@ 0__正在处理 - 在编译时出现隐式解析,因此用于给定输入的类型类实例将取决于该输入的运行时类型。

要获得所需的行为,您需要编写一些隐式定义,这些定义将反映您要在其上调用method以选择正确的类型类实例的对象的实际类型。

我认为这更像是一个维护问题而不是你现在所拥有的问题。

答案 2 :(得分:2)

简单地说:如果您想在一个地方实施您的实施,您应该为您的层次结构使用案例类:

abstract class SuperClass;

case class Clazz(...) extends SuperClass;

def method(o : SuperClass) : Map[String, Any] = o match {
   case Clazz( ... ) => defaultMethod ++ ...
   case ...
} 

(注意方法当然可以是递归的) 因为你可以在scala中使用打开 Sum类型(编译器不会警告缺少模式),这应该可以解决你的问题,而不必滥用类型类。