Dynamic Extractors in Scala

岁酱吖の 提交于 2019-12-05 00:16:43

问题


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 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)
  }
}

… 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 }
                                                                    ^

Which I find surprising, since

object Params {
  object Foo {
    def unapply{params: Map[String, String]): Option[String] = … 
  }
}

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

Should this be considered a bug?


回答1:


The canonical answer is Can extractors be customized with parameters in the body of a case statement (or anywhere else that an extractor would be used)?

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

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)

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.



来源:https://stackoverflow.com/questions/24451337/dynamic-extractors-in-scala

标签

工具导航Map