I want to be able to dynamically choose which extractors to use in my case class pattern matching.
I want something like:
def handleProcessingResult(extract: SomeType) : PartialFunction[Event, State] = {
case Event(someEvent: SomeEvent, extract(handlers)) =>
...
case Event(otherEvent: OtherEvent, extract(handlers)) =>
...
}
The idea is that I can have the above partial function, and can then use it anywhere where I know how to write an unapply
to match and extract handlers
from some pattern matched type.
If you are wondering why I want these partial functions, it is so that I can compose partial functions of common behaviour together to form the handlers for my states in an Akka FSM. This is not required to understand the question, but for example:
when(ProcessingState) {
handleProcessingResult(extractHandlersFromProcessing) orElse {
case Event(Created(path), Processing(handlers)) =>
...
}
}
when(SuspendedState) {
handleProcessingResult(extractHandlersFromSuspended) orElse {
case Event(Created(path), Suspended(waiting, Processing(handlers))) =>
...
}
It seems like this should be possible but I can't figure out how!
I have tried the following two simplifications:
object TestingUnapply {
sealed trait Thing
case class ThingA(a: String) extends Thing
case class ThingB(b: String, thingA: ThingA) extends Thing
val x = ThingA("hello")
val y = ThingB("goodbye", ThingA("maybe"))
process(x, new { def unapply(thing: ThingA) = ThingA.unapply(thing)})
process(y, new { def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)})
def process(thing: Thing, extract: { def unapply[T <: Thing](thing: T): Option[String]}) = thing match {
case extract(a) => s"The value of a is: $a"
}
}
The idea being that I should be able to pass any sub-type of Thing and a suitable extractor to process
. However, it doesn't compile due to:
[error] /tmp/proj1/TestUnapply.scala:10: type mismatch;
[error] found : AnyRef{def unapply(thing: TestingUnapply.ThingA): Option[String]}
[error] required: AnyRef{def unapply[T <: TestingUnapply.Thing](thing: T): Option[String]}
[error] process(x, new { def unapply(thing: ThingA) = ThingA.unapply(thing)})
[error] ^
[error] /tmp/proj1/TestUnapply.scala:11: type mismatch;
[error] found : AnyRef{def unapply(thing: TestingUnapply.ThingB): Option[String]}
[error] required: AnyRef{def unapply[T <: TestingUnapply.Thing](thing: T): Option[String]}
[error] process(y, new { def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)})
[error] ^
Subsequently, moving the declaration of type parameter T
onto process
, gives us:
import scala.reflect.ClassTag
object TestingUnapply {
sealed trait Thing
case class ThingA(a: String) extends Thing
case class ThingB(b: String, thingA: ThingA) extends Thing
val x = ThingA("hello")
val y = ThingB("goodbye", ThingA("maybe"))
process(x, new { def unapply(thing: ThingA) = ThingA.unapply(thing)})
process(y, new { def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)})
def process[T <: Thing: ClassTag](thing: Thing, extract: { def unapply(thing: T): Option[String]}) = thing match {
case extract(a) => s"The value of a is: $a"
}
}
Now gives us a different compilation error of:
[error] /tmp/TestUnapply.scala:18: Parameter type in structural refinement may not refer to an abstract type defined outside that refinement
[error] def process[T <: Thing: ClassTag](thing: Thing, extract: { def unapply(thing: T): Option[String]}) = thing match {
I am most likely doing something daft. Can someone help me please?