Scala 中的动态提取器 [英] Dynamic Extractors in Scala
问题描述
我不喜欢提取器的一件事是它们不能有参数.所以我不能有像 Param
这样的提取器:
One of the things I don't like about extractors is that they cannot have parameters. So I cannot have extractors like Param
in:
req match { case Param("foo")(foo) => … }
动态提取器?
这很不幸,我希望有一天它会改变,但今天早上我想我可以通过使用 Dynamic trait 来修复它.
Dynamic Extractors?
That's unfortunate, and I hope it will change some day, but then this morning I figured I could fix it by using the Dynamic trait.
object Params extends Dynamic {
def selectDynamic(name: String) = new {
def unapply(params: Map[String, String]): Option[String] = params.get(name)
}
}
...希望这能让我在这样的模式匹配语句中使用 Params:
… hoping that would allow me use Params in a pattern matching statement like this:
req match { case Params.Foo(value) =>
// matching Map("Foo" -> "Bar"), extracting "Bar" in value
它不起作用
...但它不起作用.似乎编译器仍然感到困惑.
It doesn't work
… but it doesn't work. It seems the compiler is still getting confused.
scala> Map("Foo" -> "bar") match { case Params.Foo(value) => value }
<console>:10: error: value applyDynamic is not a member of object Params
error after rewriting to Params.<applyDynamic: error>("Foo")
possible cause: maybe a wrong Dynamic method signature?
Map("Foo" -> "bar") match { case Params.Foo(value) => value }
^
<console>:10: error: not found: value value
Map("Foo" -> "bar") match { case Params.Foo(value) => value }
^
我觉得很奇怪,因为
object Params {
object Foo {
def unapply{params: Map[String, String]): Option[String] = …
}
}
会很好用.另外,如果我先将 Params.Foo
分配给一个变量,一切都会好的:
would work fine. Also, if I assign Params.Foo
to a variable first, everything is okay:
scala> val Foo = Params.Foo
Foo: AnyRef{def unapply(params: Map[String,String]): Option[String]} = Params$$anon$1@f2106d8
scala> Map("Foo" -> "bar") match { case Foo(value) => value }
warning: there were 1 feature warning(s); re-run with -feature for details
res2: String = bar
这应该被视为错误吗?
推荐答案
规范的答案是可以使用主体中的参数自定义提取器一个 case 语句(或其他任何可以使用提取器的地方)?
但是 黑客博客 提出了通过的技巧参数作为动态选择的任意名称,如问题中所尝试.
but the hacking blog suggests the trick of passing arguments as arbitrary names to dynamic selection, as tried in the question.
$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions for evaluation. Or try :help.
scala> class X(pattern: String) { val RegExp = new { def unapplySeq(s: String) = pattern.r.unapplySeq(s) } }
defined class X
scala> import language._
import language._
scala> case object p extends Dynamic { def selectDynamic(pattern: String) = new X(pattern) }
defined object p
scala> "abcdef" match { case p.`.*(b.*d).*`.RegExp(s) => s }
res0: String = bcd
需要额外的选择,因为 2.11 中的崩溃错误与问题中显示的 2.10 错误不同:
The extra selection is required because of a crashing bug in 2.11 which is different from the 2.10 error shown in the question:
scala> class RegExp(pattern: String) { def unapplySeq(s: String) = pattern.r.unapplySeq(s) }
defined class RegExp
scala> case object p extends Dynamic { def selectDynamic(pattern: String) = new RegExp(pattern) }
defined object p
scala> "abcdef" match { case p.`.*(b.*d).*`(s) => s }
java.lang.NullPointerException
at scala.tools.nsc.typechecker.PatternTypers$PatternTyper$class.inPlaceAdHocOverloadingResolution(PatternTypers.scala:68)
2.10 中的工作示例:
The working example in 2.10:
$ scala210 -language:_
Welcome to Scala version 2.10.5 (OpenJDK 64-Bit Server VM, Java 1.7.0_95).
Type in expressions to have them evaluated.
Type :help for more information.
scala> :pa
// Entering paste mode (ctrl-D to finish)
class X(key: String) { val get = new { def unapply(params: Map[String, String]): Option[String] = params.get(key) }}
object Params extends Dynamic {
def selectDynamic(name: String) = new X(name)
}
// Exiting paste mode, now interpreting.
defined class X
defined module Params
scala> Map("Foo" -> "bar") match { case Params.Foo.get(value) => value }
res0: String = bar
这与问题末尾显示的内容类似,但很明显可以通过这种方式使用动态选择.
This is similar to what is shown at the end of the question, but makes it obvious that dynamic selection can be used this way.
这篇关于Scala 中的动态提取器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!