Обычно такое сопоставление с образцом может быть записано следующим образом
data match {
case None => doSomething()
case Some(data) if data > 50 => doSomething()
case _ => doOther()
}
Если такая комбинация (None || Some(data) && data > 50
) встречается часто, вы можете ввести собственный экстрактор
object GreaterThan50OrEmpty {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > 50 => true
case _ => false
}
}
data match {
case GreaterThan50OrEmpty() => println("matches pattern")
case _ => println("default")
}
Вы даже можете назвать его как хотите
object `None || Some(data) && data > 50` {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > 50 => true
case _ => false
}
}
data match {
case `None || Some(data) && data > 50`() => println("matches pattern")
case _ => println("default")
}
Чуть более общий подход
class GreaterThanOrEmpty(dataBound: Int) {
def unapply(arg: Option[Int]): Boolean = arg match {
case None => true
case Some(data) if data > dataBound => true
case _ => false
}
}
val GreaterThan50OrEmpty = new GreaterThanOrEmpty(50)
data match {
case GreaterThan50OrEmpty() => println("matches pattern")
case _ => println("default")
}
Вы даже можете сгенерировать такие unapply
автоматически (хотя, думаю, это того не стоит)
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("enable macro paradise")
class extractor[A] extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ExtractorMacro.impl
}
object ExtractorMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val typA = c.prefix.tree match {
case q"new extractor[$a]" => a
}
annottees match {
case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
val cases = tname.decoded.split('|').map(s => s"case $s => true").mkString("\n")
val casesTree = c.parse(
s"""arg match {
| $cases
| case _ => false
|}""".stripMargin)
q"""$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
..$body
def unapply(arg: $typA): Boolean = $casesTree
}"""
case _ => c.abort(c.enclosingPosition, "not object")
}
}
}
Использование:
@extractor[Option[Int]]
object `Some(x) if x < 25 | None | Some(data) if data > 50`
//Warning:scalac: object ... extends scala.AnyRef {
// ...
// def unapply(arg: Option[Int]): Boolean = arg match {
// case Some((x @ _)) if x.$less(25) => true
// case None => true
// case Some((data @ _)) if data.$greater(50) => true
// case _ => false
// }
//}
def test(arg: Any) = arg match {
case `Some(x) if x < 25 | None | Some(data) if data > 50`() =>
println("matches pattern")
case _ => println("default")
}
test(None) // matches pattern
test(Some(51)) // matches pattern
test(Some(24)) // matches pattern
test(Some(30)) // default