Обход ограничений по перегрузке (реализация черты для нескольких типов) - PullRequest
3 голосов
/ 24 ноября 2011

Я знаю, что невозможно перегрузить методы, которые отличаются только типом возврата. Но мне интересно, есть ли какие-нибудь умные стратегии, чтобы эффективно справиться с этой ситуацией:

trait Reader[A] { def read(in: java.io.DataInput): A }

trait B; trait C

def doSomethingB()(implicit r: Reader[B]) = ()
def doSomethingC()(implicit r: Reader[C]) = ()

trait MultiReader extends Reader[B] with Reader[C] { // not possible
  implicit me = this

  doSomethingB()
  doSomethingC()
}

Под умным и эффективным я имею в виду, что я хотел бы избежать беспорядка и ненужных внутренних поколений, подобных этому:

trait MultiReader {
  implicit object RB extends Reader[B] { ... }
  implicit object RC extends Reader[C] { ... }

  doSomethingB()
  doSomethingC()
}

EDIT

Вот частичное решение. В последние дни я перечитывал эту суть Майлза Сабина, которая выглядела очень вдохновляющей. Поэтому я могу сделать следующее:

type Tagged[U] = { type Tag = U }
type @@[T, U]  = T with Tagged[U]

trait Reader[A] { def read(in: java.io.DataInput @@ A): A }

И тогда это работает:

trait MultiReader {
  def read(in: java.io.DataInput @@ B): B
  def read(in: java.io.DataInput @@ C): C
}

Но наследство все еще несколько нарушено:

trait MultiReader extends Reader[B] with Reader[C]

(не работает с "self-type MultiReader does not conform to Reader[B]'s selftype Reader[B]").

Ответы [ 2 ]

1 голос
/ 24 ноября 2011

Это все еще создает экземпляр Function1 плюс анонимный Reader для каждого требуемого параметра типа, но по крайней мере это синтаксически более сжато:

object Reader {
  implicit def fromFun[A](implicit fun: java.io.DataInput => A): Reader[A] =
    new Reader[A] { def read(in: java.io.DataInput): A = fun(in) }
}
trait Reader[A] { def read(in: java.io.DataInput): A }

def doSomethingB()(implicit r: Reader[B]): Unit = println(r.read(null))
def doSomethingC()(implicit r: Reader[C]): Unit = println(r.read(null))

trait MultiReader {
  implicit def readB(in: java.io.DataInput): B = new B { override def toString = "B" }
  implicit def readC(in: java.io.DataInput): C = new C { override def toString = "C" }

   doSomethingB()
   doSomethingC()
}

new MultiReader {} // --> B, C
0 голосов
/ 24 ноября 2011

Основная проблема заключается в том, что Scala не позволяет реализовывать общую черту более одного раза, даже если параметры типа различаются.Интересно, что хотя вызов в версии с тегами работает правильно (я могу сознательно вызвать read с вводом, помеченным B или C), он не работает при использовании структурных типов, например:

def doSomethingB()(implicit r: { def read(in: java.io.DataInput @@ B): B }) = ()
def doSomethingC()(implicit r: { def read(in: java.io.DataInput @@ C): C }) = ()

Здесь есть ошибка диспетчеризации, и оба вызовут чтение C.


Если есть ограничение, что B и C находятся в иерархии одного класса идея заключается в использовании границ:

sealed trait B; trait C extends B

trait UpDownReader[Up, Down] {
  def read[A >: Down <: Up : Manifest](in: java.io.DataInput): A
}

class MultiReader(implicit mfx: Manifest[X], mfy: Manifest[Y])
  extends UpDownReader[X, Y] {

  def read[A >: Y <: X](in: java.io.DataInput)(implicit mf: Manifest[A]): A =
     (if (mf == mfx) new X {} else new Y {}).asInstanceOf[A]
}

Это работает:

val m = new MultiReader
m.read[B](null)
m.read[C](null)

Однако я бы назвал это ни изящным, ни эффективным, учитывая «динамическое» сравнение манифестов изатем уродливый бросок на A.

...