动态传递提取器以进行模式匹配
Passing extractors dynamically for pattern matching
我希望能够动态选择在我的案例中使用哪些提取器class 模式匹配。
我想要这样的东西:
def handleProcessingResult(extract: SomeType) : PartialFunction[Event, State] = {
case Event(someEvent: SomeEvent, extract(handlers)) =>
...
case Event(otherEvent: OtherEvent, extract(handlers)) =>
...
}
我的想法是,我可以拥有上述部分功能,然后可以在我知道如何编写 unapply
的任何地方使用它来匹配并从某种模式匹配类型中提取 handlers
。
如果您想知道为什么我需要这些部分函数,那么我可以将常见行为的部分函数组合在一起,形成 Akka FSM 中状态的处理程序。这不是理解问题所必需的,但是例如:
when(ProcessingState) {
handleProcessingResult(extractHandlersFromProcessing) orElse {
case Event(Created(path), Processing(handlers)) =>
...
}
}
when(SuspendedState) {
handleProcessingResult(extractHandlersFromSuspended) orElse {
case Event(Created(path), Suspended(waiting, Processing(handlers))) =>
...
}
看起来这应该是可行的,但我不知道如何实现!
我尝试了以下两种简化方法:
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"
}
}
我的想法是我应该能够将 Thing 的任何子类型和合适的提取器传递给 process
。但是,由于以下原因,它无法编译:
[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] ^
随后,将类型参数 T
的声明移动到 process
上,得到:
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"
}
}
现在给我们一个不同的编译错误:
[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 {
我很可能在做一些愚蠢的事情。有人可以帮我吗?
尝试根据您的第一个简化来解决问题,希望对您有所帮助。
object DynamicPattern extends App {
sealed trait Thing
case class ThingA(a: String) extends Thing
case class ThingB(b: String, thingA: ThingA) extends Thing
// change structural type to an abstract class
abstract class UniversalExtractor[T <: Thing] {
def unapply(thing: T): Option[String]
}
// extract is an instance of UniversalExtractor with unapply method
// naturally it's an extractor
def process[T <: Thing](thing: T, extract: UniversalExtractor[T]) =
thing match {
case extract(a) => s"The value of a is: $a"
}
val x = ThingA("hello")
val y = ThingB("goodbye", ThingA("maybe"))
val result1 = process(
x,
new UniversalExtractor[ThingA] {
def unapply(thing: ThingA) = ThingA.unapply(thing)
}
)
val result2 = process(y,
new UniversalExtractor[ThingB] {
def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)
}
)
// result1 The value of a is: hello
println(" result1 " + result1)
// result2 The value of a is: maybe
println(" result2 " + result2)
}
更新
一个可能 "nasty" 的方法,没有使用抽象 class 或我稍后解释的类型一致性问题暗示的特征。
// when invoking process method, we actually know which subtype of
// Thing is pattern-matched, plus the type conformance problem,
// so here comes the ```unapply[T <: Thing](thing: T)```
// and ```asInstanceOf(which is usually not that appealing)```.
val thingAExtractor = new {
def unapply[T <: Thing](thing: T): Option[String] =
ThingA.unapply(thing.asInstanceOf[ThingA])
}
val thingBExtractor = new {
def unapply[T <: Thing](thing: T): Option[String] =
ThingB.unapply(thing.asInstanceOf[ThingB]).map(_._2.a)
}
// hello
println(process(x, thingAExtractor))
// maybe
println(process(y, thingBExtractor))
两次化简都不行的原因(其实就是在想办法的时候脑子里突然冒出这么恶心的方法,所以就写在这里,以防有帮助)。
对于第一个简化:这是关于type conformance
的问题。
type ExtractType = { def unapply[T <: Thing](thing: T): Option[String] }
val anonExtractor = new { def unapply(thing: ThingA) = ThingA.unapply(thing) }
import scala.reflect.runtime.{ universe => ru }
import scala.reflect.runtime.universe.{ TypeTag, typeTag }
def getTypeTag[T: TypeTag](o: T) = typeTag[T].tpe
def getTypeTag[T: TypeTag] = ru.typeOf[T]
// false | so in turn type check fails at compilation time
println(getTypeTag(anonExtractor) <:< getTypeTag[ExtractType])
ScalaDoc Reflection 的 Runtime 类 in Java vs. Runtime Types in Scala 部分演示了类似情况下的类型一致性。简而言之,Scala 编译器创建合成的 classes,在运行时代替用户定义的 classes,在该部分提到的情况下被翻译成等效的 Java 字节码。
对于第二个简化:这个postparameter-type-in-structural-refinement-may-not-refer-to-an-abstract-type-define-outside已经给出了一些详细的解释
我希望能够动态选择在我的案例中使用哪些提取器class 模式匹配。
我想要这样的东西:
def handleProcessingResult(extract: SomeType) : PartialFunction[Event, State] = {
case Event(someEvent: SomeEvent, extract(handlers)) =>
...
case Event(otherEvent: OtherEvent, extract(handlers)) =>
...
}
我的想法是,我可以拥有上述部分功能,然后可以在我知道如何编写 unapply
的任何地方使用它来匹配并从某种模式匹配类型中提取 handlers
。
如果您想知道为什么我需要这些部分函数,那么我可以将常见行为的部分函数组合在一起,形成 Akka FSM 中状态的处理程序。这不是理解问题所必需的,但是例如:
when(ProcessingState) {
handleProcessingResult(extractHandlersFromProcessing) orElse {
case Event(Created(path), Processing(handlers)) =>
...
}
}
when(SuspendedState) {
handleProcessingResult(extractHandlersFromSuspended) orElse {
case Event(Created(path), Suspended(waiting, Processing(handlers))) =>
...
}
看起来这应该是可行的,但我不知道如何实现!
我尝试了以下两种简化方法:
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"
}
}
我的想法是我应该能够将 Thing 的任何子类型和合适的提取器传递给 process
。但是,由于以下原因,它无法编译:
[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] ^
随后,将类型参数 T
的声明移动到 process
上,得到:
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"
}
}
现在给我们一个不同的编译错误:
[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 {
我很可能在做一些愚蠢的事情。有人可以帮我吗?
尝试根据您的第一个简化来解决问题,希望对您有所帮助。
object DynamicPattern extends App {
sealed trait Thing
case class ThingA(a: String) extends Thing
case class ThingB(b: String, thingA: ThingA) extends Thing
// change structural type to an abstract class
abstract class UniversalExtractor[T <: Thing] {
def unapply(thing: T): Option[String]
}
// extract is an instance of UniversalExtractor with unapply method
// naturally it's an extractor
def process[T <: Thing](thing: T, extract: UniversalExtractor[T]) =
thing match {
case extract(a) => s"The value of a is: $a"
}
val x = ThingA("hello")
val y = ThingB("goodbye", ThingA("maybe"))
val result1 = process(
x,
new UniversalExtractor[ThingA] {
def unapply(thing: ThingA) = ThingA.unapply(thing)
}
)
val result2 = process(y,
new UniversalExtractor[ThingB] {
def unapply(thing: ThingB) = ThingB.unapply(thing).map(_._2.a)
}
)
// result1 The value of a is: hello
println(" result1 " + result1)
// result2 The value of a is: maybe
println(" result2 " + result2)
}
更新
一个可能 "nasty" 的方法,没有使用抽象 class 或我稍后解释的类型一致性问题暗示的特征。
// when invoking process method, we actually know which subtype of
// Thing is pattern-matched, plus the type conformance problem,
// so here comes the ```unapply[T <: Thing](thing: T)```
// and ```asInstanceOf(which is usually not that appealing)```.
val thingAExtractor = new {
def unapply[T <: Thing](thing: T): Option[String] =
ThingA.unapply(thing.asInstanceOf[ThingA])
}
val thingBExtractor = new {
def unapply[T <: Thing](thing: T): Option[String] =
ThingB.unapply(thing.asInstanceOf[ThingB]).map(_._2.a)
}
// hello
println(process(x, thingAExtractor))
// maybe
println(process(y, thingBExtractor))
两次化简都不行的原因(其实就是在想办法的时候脑子里突然冒出这么恶心的方法,所以就写在这里,以防有帮助)。
对于第一个简化:这是关于type conformance
的问题。
type ExtractType = { def unapply[T <: Thing](thing: T): Option[String] }
val anonExtractor = new { def unapply(thing: ThingA) = ThingA.unapply(thing) }
import scala.reflect.runtime.{ universe => ru }
import scala.reflect.runtime.universe.{ TypeTag, typeTag }
def getTypeTag[T: TypeTag](o: T) = typeTag[T].tpe
def getTypeTag[T: TypeTag] = ru.typeOf[T]
// false | so in turn type check fails at compilation time
println(getTypeTag(anonExtractor) <:< getTypeTag[ExtractType])
ScalaDoc Reflection 的 Runtime 类 in Java vs. Runtime Types in Scala 部分演示了类似情况下的类型一致性。简而言之,Scala 编译器创建合成的 classes,在运行时代替用户定义的 classes,在该部分提到的情况下被翻译成等效的 Java 字节码。
对于第二个简化:这个postparameter-type-in-structural-refinement-may-not-refer-to-an-abstract-type-define-outside已经给出了一些详细的解释