英文:
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 <: 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.
答案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 <: 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
}
and have implementations of two sorts
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 = ???
}
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 <: 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 {})
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 <: 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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论