I provide here an extended example (which shows more of my context) based on Neil Essy's closed response type approach:
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] }
Then the following script compiles with not too much interference:
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)) } }
... and gives the desired results:
val t = new Test t.renamed() t.moved()
Now the only thing that I donβt get, and I find ugly, is that my cases should look like
case _: keyCaseObject.type =>
and cannot be
case keyCaseObject =>
which I would prefer. Any ideas this limitation comes from?
source share