首页 > 解决方案 > scala中的嵌套多态性

问题描述

我需要一个带有方法的隐式类,它可以让我合并任何<: Map可能具有重复键和多态值的不可变映射类型 ()。我无法弄清楚让隐式类使用嵌套的多态类型并隐式工作(类似于A <: Map[_, B], B <: Combinable[B])。

我可以让它适用于所有 Map 类型......或多态值......但不能同时适用。我无法弄清楚如何组合成一个隐式类而不会出现在隐式类中找不到方法的错误。

所以例如...

trait Combinable[A] {
  this: A =>

  def combine(that: A): A

  def combine(that: Option[A]): A = that match {
    case Some(a) => this combine a
    case None => this
  }
}

所以假设我有一堂课......

case class Meta(???) extends Combinable[Meta] {
  def combine(that: Meta): Meta = ???
}

现在,如果我有一个标准Map的 immutable ,那就很简单了……效果很好。

implicit class CombinableMaps[A <: Combinable[A]](val m1: Map[String, A]) {
  def mergeMaps(m2: Map[String, A]): Map[String, A] = {  
    m1 ++
    m2.map { case (k,v) => k -> (v combine m1.get(k)) }
  }.asInstanceOf[Map[String, A]]
}

但是,如果我希望它也适用于TreeMaps 和SortedMaps 以及其他任何东西怎么办?

implicit class CombinableMaps[B <: Combinable[B], A <: Map[String,B]](val m1: A) {
  def mergeMaps(m2: A): A = {  
    m1 ++
    m2.map { case (k,v) => k -> (v combine m1.get(k)) }
  }.asInstanceOf[A]
}

这编译没有错误,但是当我尝试使用mergeMap它抛出的方法时error: value mergeMaps is not a member of Map[String,Meta]

我尝试了一种变体,其中B将类型传递给Alike A[B]... 再次编译(如果我导入scala.language.higherKinds了)但没有被应用。

这种嵌套多态是否允许?我什至无法弄清楚要搜索什么词。

提前致谢。

标签: scalapolymorphismimplicit

解决方案


解决问题的关键是不要试图A立即推断。即使是简单的事情也会导致类型推断:

class Foo[B, A <: Map[String, B]](val a: A)
new Foo(Map("foo" -> 42))

inferred type arguments [Nothing,scala.collection.immutable.Map[String,Int]] do not conform to value 's type parameter bounds [B,A <: Map[String,B]]

type mismatch;
 found   : scala.collection.immutable.Map[String,Int]
 required: A

这是因为类型推断在“层”中起作用:它AB检查A. 解决方案是对 A 进行不同的定义:

class Foo[B, A[X] <: Map[String, X]](val a: A[B])
new Foo(Map("foo" -> 42))

现在让我们看看你的问题。您的Combinabletrait 通常会被类型类替换,这样您就不必扩展您需要组合Combinable的每种类型。A您可以隐Combinable式地为您需要的每种类型提供隐式。这完全是可选的,如果你愿意,你可以选择坚持你的特质。

trait Combinable[A] {
  def combine(first: A, second: A): A
  def combine(first: A, second: Option[A]): A = second match {
    case Some(a) => combine(first, a)
    case None => first
  }
}

case class Meta(x: Int)
object Meta {
  implicit val combinableInstance: Combinable[Meta] = (first, second) => Meta(first.x + second.x)
}


implicit class CombinableMaps[B : Combinable, A[X] <: Map[String,X]](val m1: A[B]) {
  def mergeMaps(m2: A[B]): A[B] = {  
    m1 ++
    m2.map { case (k,v) => k -> implicitly[Combinable[B]].combine(v,m1.get(k)) }
  }.asInstanceOf[A[B]]
}

import collection.immutable.TreeMap
val m = TreeMap("foo" -> Meta(42), "bar" -> Meta(43))
val m2 = TreeMap("bar" -> Meta(43))
val m3: TreeMap[String,Meta] = m.mergeMaps(m2)

推荐阅读