【问题标题】:Dynamic Extractors in ScalaScala 中的动态提取器
【发布时间】:2016-06-18 00:51:23
【问题描述】:

我不喜欢提取器的一件事是它们不能有参数。所以我不能有像Param 这样的提取器:

req match { case Param("foo")(foo) => … }

动态提取器?

这很不幸,我希望它有一天会改变,但今天早上我想我可以通过使用 Dynamic trait 来解决它。

object Params extends Dynamic {
  def selectDynamic(name: String) = new {
    def unapply(params: Map[String, String]): Option[String] = params.get(name)
  }
}

...希望这能让我在这样的模式匹配语句中使用 Params:

req match { case Params.Foo(value) => 
  // matching Map("Foo" -> "Bar"), extracting "Bar" in value

它不起作用

……但它不起作用。编译器似乎仍然感到困惑。

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 分配给一个变量,一切都会好起来的:

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

这应该被认为是一个错误吗?

【问题讨论】:

  • 当我尝试用你的object Params extends Dynamic 编译Map("" -&gt; "") match { case Params.Foo(value) =&gt; 5 } 时,编译器会因为NullPointerException 而崩溃——对我来说绝对是个错误。
  • @wingedsubmariner 从我的 Scala REPL 的输出中可以看出,这不是我的方式 NullpointerException
  • 我不会在这里尝试与语言语法作斗争,而是简单地将提取器分配给一个常量并在 match 块中使用该常量;在这里诉诸 Dynamic 看起来是个坏主意,在我看来,这会导致无法读取的非 Scalaesque 代码,而 Dynamic 并不适用。
  • @ErikAllik 我一直在使用像Params.Foo 这样的提取器作为嵌套对象。我看不出这是“不可读”和“非Scalaesque”的。 Unfiltered 一直使用它,也用于extracting parameters,所以我不明白为什么你不应该尝试让语法不那么冗长和笨拙。此外,ScalaDocs 没有提及 Dynamic 的用途,除了一个非常通用的描述,完全符合我的用例。
  • 同意。最终的解决方案是参数化提取器,根据this,它应该在 Scala 2.11 中,但我还没有在发行说明中找到任何参考。

标签: scala


【解决方案1】:

标准答案是Can extractors be customized with parameters in the body of a case statement (or anywhere else that an extractor would be used)?

the hacking blog 建议将参数作为任意名称传递给动态选择的技巧,正如问题中所尝试的那样。

$ 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 中的 a crashing bug 与问题中显示的 2.10 错误不同,因此需要额外选择:

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 中的工作示例:

$ 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

这类似于问题末尾显示的内容,但很明显可以通过这种方式使用动态选择。

【讨论】:

    猜你喜欢
    • 2018-06-02
    • 1970-01-01
    • 2017-04-13
    • 1970-01-01
    • 2011-08-21
    • 2011-07-18
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多