Я привожу здесь расширенный пример (который показывает больше моего контекста) на основе подхода закрытых типов ответа Нила Эсси:
trait KeyLike { def id: Int }
trait DispatchCompanion {
private var cnt = 0
sealed trait Value
sealed trait Key[V <: Value] extends KeyLike {
val id = cnt // automatic incremental ids
cnt += 1
}
}
trait Event[V] {
def apply(): Option[V] // simple imperative invocation for testing
}
class EventImpl[D <: DispatchCompanion, V <: D#Value](
disp: Dispatch[D], key: D#Key[V]) extends Event[V] {
def apply(): Option[V] = disp.pull(key)
}
trait Dispatch[D <: DispatchCompanion] {
// factory method for events
protected def event[V <: D#Value](key: D#Key[V]): Event[V] =
new EventImpl[D, V](this, key)
def pull[V <: D#Value](key: D#Key[V]): Option[V]
}
Затем следующий сценарий компилируется с не слишком большим количеством беспорядка:
object Test extends DispatchCompanion {
case class Renamed(before: String, now: String) extends Value
case class Moved (before: Int , now: Int ) extends Value
private case object renamedKey extends Key[Renamed]
private case object movedKey extends Key[Moved ]
}
class Test extends Dispatch[Test.type] {
import Test._
val renamed = event(renamedKey)
val moved = event(movedKey )
// some dummy propagation for testing
protected def pullRenamed: (String, String) = ("doesn't", "matter")
protected def pullMoved : (Int , Int ) = (3, 4)
def pull[V <: Value](key: Key[V]): Option[V] = key match {
case _: renamedKey.type => val p = pullRenamed; Some(Renamed(p._1, p._2))
case _: movedKey.type => val p = pullMoved; Some(Moved( p._1, p._2))
}
}
... и дает желаемые результаты:
val t = new Test
t.renamed()
t.moved()
Теперь единственное, что я не понимаю и нахожу уродливым, это то, что мои дела должны иметь форму
case _: keyCaseObject.type =>
и не может быть
case keyCaseObject =>
, что я бы очень предпочел.Есть идеи, откуда это ограничение?