Me encuentro con frecuencia usando la coincidencia de patrones que devuelve un Option sin que el caso de coincidencia devuelva None, p.

x match {
    case A(a) => Some(a)
    case B(b) => Some(b)
    case _ => None
}

Me imagino simplificando esto usando

object MaybeMatchImplicits {
    implicit class MaybeMatcher[A](val underlying: A) extends AnyVal {
        @inline 
        def maybeMatch[B](f: PartialFunction[A, B]): Option[B] = f.lift.apply(underlying)
    }
}

Que permite

scala> import MaybeMatchImplicits._
import MaybeMatchImplicits._

scala> 5 maybeMatch { case 5 => 'good }
res0: Option[Symbol] = Some('good)

scala> 6 maybeMatch { case 5 => 'good }
res1: Option[Symbol] = None

Me pregunto si este enfoque oculta algún problema y / o si hay un mecanismo más simple / mejor / más idiomático para hacer esto en Scala 2.11+.

Actualización: El objetivo es manejar el cálculo arbitrario en la HR de las coincidencias, lo que hace que las soluciones basadas en excepciones sean indeseables.

4
Sim 29 dic. 2016 a las 07:06

3 respuestas

La mejor respuesta

Idiomática:

scala> case class A(a: Int) ; case class B(b: String)
defined class A
defined class B

scala> def f(x: Any) = Option(x) collect { case A(a) => a ; case B(b) => b }
f: (x: Any)Option[Any]

scala> f(42)
res0: Option[Any] = None

scala> f(A(42))
res1: Option[Any] = Some(42)

scala> f(B("ok"))
res2: Option[Any] = Some(ok)

Alternativamente:

scala> import PartialFunction.{cond => when, condOpt => whenever}
import PartialFunction.{cond=>when, condOpt=>whenever}

scala> def f(x: Any) = whenever(x) { case A(a) => a ; case B(b) => b }
f: (x: Any)Option[Any]

scala> f(42)
res3: Option[Any] = None

scala> f(A(42))
res4: Option[Any] = Some(42)

scala> f(B("ok"))
res5: Option[Any] = Some(ok)
6
som-snytt 29 dic. 2016 a las 05:23

Lo haría así:

def trial(a:Any, f:Any => Any) = try Some(f(a)) catch {case _:MatchError => None}

implicit class BetterAny(a:Any) {
   def betterMatch(f:Any => Any) = trial(a, f)
}

// some example classes
case class A(i:Int)
case class B(s:String)

En el REPL:

scala> A(1) betterMatch {case A(a) => a; case B(b) => b}
res11: Option[Any] = Some(1)

scala> 2 betterMatch {case A(a) => a; case B(b) => b}
res12: Option[Any] = None
0
Jus12 30 dic. 2016 a las 08:26

Recoger de la opción

Utilice el método get (consulte la implementación que se proporciona a continuación) que envuelve el valor dado alrededor de la opción y luego recopila el valor requerido.

Envuelva el valor usando la opción y luego recolecte lo que quiera recolectar.

Option(x: Any).collect { case 1 => 1 }

O

x get { case 2 => 2 } // get implementation is given below

Scala REPL

scala> Option(1).collect { case 1 => 1 }
res0: Option[Int] = Some(1)

scala> Option(2).collect { case str: String => "bad" }
<console>:12: error: scrutinee is incompatible with pattern type;
 found   : String
 required: Int
       Option(2).collect { case str: String => "bad" }
                                     ^

scala> Option(2: Any).collect { case str: String => "bad" }
res2: Option[String] = None

scala> Option(2: Any).collect { case 2 => "bad" }
res3: Option[String] = Some(bad)

API más agradable con clase implícita

implicit class InnerValue[A](value: A) {
  def get[B](pf: PartialFunction[Any, B]): Option[B] = Option(value) collect pf
}

Scala REPL

scala> implicit class InnerValue[A](value: A) {
     |   def get[B](pf: PartialFunction[Any, B]): Option[B] = Option(value) collect pf
     | }
defined class InnerValue

scala> 2.get { case 2 => 2}
res5: Option[Int] = Some(2)

scala> 2.get { case 3 => 2}
res6: Option[Int] = None

Ahora puede simplemente invoke obtener el método y pasar la función parcial. Ahora puede obtener un valor envuelto en Some o obtendrá None.

Tenga en cuenta que la API anterior (método get) no es de tipo seguro, puede hacer

 2.get { case str: String => str }

Lo que devuelve Ninguno.

Ahora, si desea escribir con seguridad, realice el siguiente cambio

Tipo de seguridad

 implicit class InnerValue[A](value: A) {
  def get[B](pf: PartialFunction[A, B]): Option[B] = Option(value) collect pf
 }

Observe en la función parcial que el tipo de parámetro de entrada es A en lugar de Any.

Ahora cuando lo haces

2.get { case str: String => str }

Obtendrá un error de compilación.

scala>      2.get { case str: String => str }
<console>:15: error: scrutinee is incompatible with pattern type;
 found   : String
 required: Int
            2.get { case str: String => str }

Evitar el error de compilación

Puede evitar el error de compilación haciendo lo siguiente

scala> (2: Any) get { case str: String => str}
res16: Option[String] = None
2
pamu 29 dic. 2016 a las 11:06