解决Scala中的方法重载错误,使用类型系统。

huangapple go评论58阅读模式
英文:

Solving method overloading error in scala with the type system

问题

I have some code that compares a document with another and outputs pointers to fields that have changed, with the old and new values.

Some elements of the document have a sequenceId captured by this trait:

trait ExplicitlySequencedObject {
  val sequenceId: Int
}

Elements in the document that can be compared also extend a utility trait called DiffTools which includes the following method signatures:

trait DiffTools[T] {
  def createDiff[E <: DiffTools[E]](original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff

  def createDiff[E <: DiffTools[E]](original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff
}

Some elements are not explicitly sequenced, but where they are I want createDiff to work differently from this implementation, so method signatures like the following are added to DiffTools in addition to the above:

def createDiff[S <: DiffTools[S] with ExplicitlySequencedObject](
  original: Seq[S],
  current: Seq[S],
  pointerString: DocumentFieldPointer
): DocumentDiff

def createDiff[S <: DiffTools[S] with ExplicitlySequencedObject](
  original: Option[Seq[S]],
  current: Option[Seq[S]],
  pointerString: DocumentFieldPointer
): DocumentDiff

When I do this I get a compiler error method createDiff is defined twice and I'm guessing this is related to type erasure? I'd like to know why exactly this doesn't work.

Anyway, I've solved the problem for now by just renaming the method (KISS, right?) but I'm thinking this is not the best Scala has to offer in solving this with its fancy type system, so other approaches would be appreciated.

英文:

I have some code that compares a document with another and outputs pointers to fields that have changed, with the old and new values.

Some elements of the document have a sequenceId captured by this trait:

trait ExplicitlySequencedObject {
  val sequenceId: Int
}

Elements in the document that can be compared also extend a utility trait called DiffTools which includes the following method signatures:

trait DiffTools[T] {
def createDiff[E &lt;: DiffTools[E]](original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff

def createDiff[E &lt;: DiffTools[E]](original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff
}

Some elements are not explicitly sequenced, but where they are I want createDiff to work differently from this implementation, so method signatures like the following are added to DiffTools in addition to the above:

  def createDiff[S &lt;: DiffTools[S] with ExplicitlySequencedObject](
    original: Seq[S],
    current: Seq[S],
    pointerString: DocumentFieldPointer
  ): DocumentDiff

  def createDiff[S &lt;: DiffTools[S] with ExplicitlySequencedObject](
    original: Option[Seq[S]],
    current: Option[Seq[S]],
    pointerString: DocumentFieldPointer
  ): DocumentDiff

When I do this I get a compiler error method createDiff is defined twice and I'm guessing this is related to type erasure? I'd like to know why exactly this doesn't work.

Anyway I've solved the problem for now by just renaming the method (KISS, right?) but I'm thinking this is not the best scala has to offer in solving this with its fancy type system, so other approaches would be appreciated.

答案1

得分: 2

以下是您提供的代码的中文翻译:

例如,您可以添加一个更多的类型参数:

trait DiffTools[T, Upper] {
  def createDiff[E <: DiffTools[E, Upper] with Upper](original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff
  def createDiff[E <: DiffTools[E, Upper] with Upper](original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff
}

并且有两种类型的实现:

class DiffToolsImpl[T] extends DiffTools[T, Any] {
  override def createDiff[E <: DiffTools[E, Any]](original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff = ???
  override def createDiff[E <: DiffTools[E, Any]](original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff = ???
}

class ExplicitlySequencedDiffToolsImpl[T] extends DiffTools[T, ExplicitlySequencedObject] {
  override def createDiff[S <: DiffTools[S, ExplicitlySequencedObject] with ExplicitlySequencedObject](
    original: Seq[S],
    current: Seq[S],
    pointerString: DocumentFieldPointer
  ): DocumentDiff = ???

  override def createDiff[S <: DiffTools[S, ExplicitlySequencedObject] with ExplicitlySequencedObject](
    original: Option[Seq[S]],
    current: Option[Seq[S]],
    pointerString: DocumentFieldPointer
  ): DocumentDiff = ???
}

您还可以考虑用类型类替换 F-bounds 和重载(一种特殊的多态性),参考类型类(另一种特殊的多态性)的方式:

trait DiffTools[A] {
  def createDiff(original: A, current: A, pointerString: DocumentFieldPointer): DocumentDiff
}

trait LowPriorityDiffTools {
  implicit def seq[E]: DiffTools[Seq[E]] = new DiffTools[Seq[E]] {
    override def createDiff(original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff = ???
  }
  implicit def optSeq[E]: DiffTools[Option[Seq[E]]] = new DiffTools[Option[Seq[E]]] {
    override def createDiff(original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff = ???
  }
}

object DiffTools extends LowPriorityDiffTools {
  implicit def explicitSeq[E <: ExplicitlySequencedObject]: DiffTools[Seq[E]] = new DiffTools[Seq[E]] {
    override def createDiff(original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff = ???
  }
  implicit def explicitOptSeq[E <: ExplicitlySequencedObject]: DiffTools[Option[Seq[E]]] = new DiffTools[Option[Seq[E]]] {
    override def createDiff(original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff = ???
  }

  def createDiff[A](original: A, current: A, pointerString: DocumentFieldPointer)(implicit diffTools: DiffTools[A]): DocumentDiff =
    diffTools.createDiff(original, current, pointerString)
}

class ExplicitlySequencedObjectImpl extends ExplicitlySequencedObject {
  override val sequenceId: Int = 100
}

val opt: Option[Seq[ExplicitlySequencedObjectImpl]] = Some(Seq(new ExplicitlySequencedObjectImpl()))
DiffTools.createDiff(opt, opt, new DocumentFieldPointer {})

类型类将是最灵活的方法。最小更改您原始代码的方式是使用 DummyImplicit,@LuisMiguelMejíaSuárez 是正确的:

trait DiffTools[T] {
  def createDiff[E <: DiffTools[E]](original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff

  def createDiff[E <: DiffTools[E]](original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff

  def createDiff[S <: DiffTools[S] with ExplicitlySequencedObject](
    original: Seq[S],
    current: Seq[S],
    pointerString: DocumentFieldPointer
  )(implicit dummyImplicit: DummyImplicit): DocumentDiff

  def createDiff[S <: DiffTools[S] with ExplicitlySequencedObject](
    original: Option[Seq[S]],
    current: Option[Seq[S]],
    pointerString: DocumentFieldPointer
  )(implicit dummyImplicit: DummyImplicit): DocumentDiff
}
英文:

For example you can add one more type parameter

trait DiffTools[T, Upper] {
def createDiff[E &lt;: DiffTools[E, Upper] with Upper](original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff
def createDiff[E &lt;: DiffTools[E, Upper] with Upper](original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff
}

and have implementations of two sorts

class DiffToolsImpl[T] extends DiffTools[T, Any] {
override def createDiff[E &lt;: DiffTools[E, Any]](original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff = ???
override def createDiff[E &lt;: DiffTools[E, Any]](original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff = ???
}
class ExplicitlySequencedDiffToolsImpl[T] extends DiffTools[T, ExplicitlySequencedObject] {
override def createDiff[S &lt;: DiffTools[S, ExplicitlySequencedObject] with ExplicitlySequencedObject](
original: Seq[S],
current: Seq[S],
pointerString: DocumentFieldPointer
): DocumentDiff = ???
override def createDiff[S &lt;: DiffTools[S, ExplicitlySequencedObject] with ExplicitlySequencedObject](
original: Option[Seq[S]],
current: Option[Seq[S]],
pointerString: DocumentFieldPointer
): DocumentDiff = ???
}

Also you could comsider to replace F-bounds and overloading (one flavor of ad hoc polymorhism) with type classes (another flavor of ad hoc polymorphism)

http://tpolecat.github.io/2015/04/29/f-bounds.html

Something like

trait DiffTools[A] {
def createDiff(original: A, current: A, pointerString: DocumentFieldPointer): DocumentDiff
}
trait LowPriorityDiffTools {
implicit def seq[E]: DiffTools[Seq[E]] = new DiffTools[Seq[E]] {
override def createDiff(original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff = ???
}
implicit def optSeq[E]: DiffTools[Option[Seq[E]]] = new DiffTools[Option[Seq[E]]] {
override def createDiff(original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff = ???
}
}
object DiffTools extends LowPriorityDiffTools {
implicit def explicitSeq[E &lt;: ExplicitlySequencedObject]: DiffTools[Seq[E]] = new DiffTools[Seq[E]] {
override def createDiff(original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff = ???
}
implicit def explicitOptSeq[E &lt;: ExplicitlySequencedObject]: DiffTools[Option[Seq[E]]] = new DiffTools[Option[Seq[E]]] {
override def createDiff(original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff = ???
}
def createDiff[A](original: A, current: A, pointerString: DocumentFieldPointer)(implicit diffTools: DiffTools[A]): DocumentDiff =
diffTools.createDiff(original, current, pointerString)
}
class ExplicitlySequencedObjectImpl extends ExplicitlySequencedObject {
override val sequenceId: Int = 100
}
val opt: Option[Seq[ExplicitlySequencedObjectImpl]] = Some(Seq(new ExplicitlySequencedObjectImpl()))
DiffTools.createDiff(opt, opt, new DocumentFieldPointer {})

Type classes would be the most flexible approach.

The minimal change to your original code would be DummyImplicit, @LuisMiguelMejíaSuárez is correct

trait DiffTools[T] {
def createDiff[E &lt;: DiffTools[E]](original: Seq[E], current: Seq[E], pointerString: DocumentFieldPointer): DocumentDiff
def createDiff[E &lt;: DiffTools[E]](original: Option[Seq[E]], current: Option[Seq[E]], pointerString: DocumentFieldPointer): DocumentDiff
def createDiff[S &lt;: DiffTools[S] with ExplicitlySequencedObject](
original: Seq[S],
current: Seq[S],
pointerString: DocumentFieldPointer
)(implicit dummyImplicit: DummyImplicit): DocumentDiff
def createDiff[S &lt;: DiffTools[S] with ExplicitlySequencedObject](
original: Option[Seq[S]],
current: Option[Seq[S]],
pointerString: DocumentFieldPointer
)(implicit dummyImplicit: DummyImplicit): DocumentDiff
}

huangapple
  • 本文由 发表于 2023年4月19日 23:12:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76056142.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定