组合多个提取器对象以在一个匹配语句中使用

Dyl*_*lan 3 scala

是否可以在一个匹配语句中运行多个提取器?

object CoolStuff {
  def unapply(thing: Thing): Option[SomeInfo] = ...
}
object NeatStuff {
  def unapply(thing: Thing): Option[OtherInfo] = ...
}

// is there some syntax similar to this?
thing match {
  case t @ CoolStuff(someInfo) @ NeatStuff(otherInfo) => process(someInfo, otherInfo)
  case _ => // neither Cool nor Neat
}
Run Code Online (Sandbox Code Playgroud)

这里的意图是有两个提取器,我不必做这样的事情:

object CoolNeatStuff {
  def unapply(thing: Thing): Option[(SomeInfo, OtherInfo)] = thing match {
    case CoolStuff(someInfo) => thing match {
      case NeatStuff(otherInfo) => Some(someInfo -> otherInfo)
      case _ => None // Cool, but not Neat
    case _ => None// neither Cool nor Neat
  }
}
Run Code Online (Sandbox Code Playgroud)

cch*_*tep 6

可以尝试

object ~ {
  def unapply[T](that: T): Option[(T,T)] = Some(that -> that)
}

def too(t: Thing) = t match {
  case CoolStuff(a) ~ NeatStuff(b) => ???
}
Run Code Online (Sandbox Code Playgroud)


And*_*kin 5

我提出了一个非常相似的解决方案,但我有点太慢了,所以我没有将其发布为答案。然而,由于 @userunknown 要求解释它是如何工作的,所以我无论如何都会在这里转储类似的代码,并添加一些注释。也许有人发现它是 cchantep 简约解决方案的一个有价值的补充(它看起来……书法?出于某种原因,从某种意义上来说)。

所以,这是我的类似的、美学上不太令人愉悦的建议:

object && {
  def unapply[A](a: A) = Some((a, a))
}

// added some definitions to make your question-code work
type Thing = String
type SomeInfo = String
type OtherInfo = String

object CoolStuff {
  def unapply(thing: Thing): Option[SomeInfo] = Some(thing.toLowerCase)
}
object NeatStuff {
  def unapply(thing: Thing): Option[OtherInfo] = Some(thing.toUpperCase)
}

def process(a: SomeInfo, b: OtherInfo) = s"[$a, $b]"

val res = "helloworld" match {
  case CoolStuff(someInfo) && NeatStuff(otherInfo) =>
     process(someInfo, otherInfo)
  case _ =>
}

println(res)
Run Code Online (Sandbox Code Playgroud)

这打印

[helloworld, HELLOWORLD]
Run Code Online (Sandbox Code Playgroud)

这个想法是标识符(特别&&~在 cchantep 的代码中)可以用作paths 中的中缀运算符。因此,match-case

case CoolStuff(someInfo) && NeatStuff(otherInfo) =>
Run Code Online (Sandbox Code Playgroud)

将被脱糖成

case &&(CoolStuff(someInfo), NeatStuff(otherInfo)) => 
Run Code Online (Sandbox Code Playgroud)

然后unapply方法方法&&将被调用,它只是复制其输入。

在我的代码中,复制是通过简单的Some((a, a)). 在 cchantep 的代码中,它是用更少的括号完成的:Some(t -> t)。箭头->来自ArrowAssoc,它又作为 中的隐式转换提供Predef。这只是创建对的快速方法,通常在地图中使用:

Map("hello" -> 42, "world" -> 58)
Run Code Online (Sandbox Code Playgroud)

另注:注意&&可以多次使用:

case Foo(a) && Bar(b) && Baz(c) => ...
Run Code Online (Sandbox Code Playgroud)

所以...我不知道这是否是对 cchantep 答案的答案或扩展评论,但也许有人发现它有用。