Coincidencia de patrones de múltiples tipos de Scala

80

Me pregunto cómo puedo utilizar la coincidencia de patrones de varios tipos. Yo tengo:

abstract class MyAbstract

case class MyFirst extends MyAbstract
case class MySecond extends MyAbstract
case class MyThird extends MyAbstract // shouldn't be matched and shouldn't call doSomething()

val x: MyAbstract = MyFirst

x match { 
 case a: MyFirst => doSomething()
 case b: MySecond => doSomething()
 case _ => doSomethingElse()
}

Entonces me gustaría escribir algo como:

x match {
 case a @ (MyFirst | MySecond) => doSomething()
 case _ => doSomethingElse()
}

Vi una construcción similar en algún tutorial, pero me da un error:

pattern type is incompatible with expected type;
[error]  found   : object MyFirst
[error]  required: MyAbstract

Entonces, ¿hay alguna manera de definir algunos tipos diferentes en la cláusula on case? Creo que haría el código más bonito. Como si tuviera 5 de ellos, escribiré el mismo código 5 veces (llamando a doSomething ()).

¡Gracias por adelantado!

psisoyev
fuente
Creo que es un problema XY; tiene una superclase común para todos los doSomethingcasos, ¿por qué no coincidir con el case a : MyAbstractentonces ...?
Patryk Ćwiek
Lo siento, olvidé mencionar que tengo otras clases que amplían la clase MyAbstract y no debería llamar a doSomething.
psisoyev
Oh, está bien, solo quería aclarar eso :) Sin embargo, ahora tienes una respuesta correcta a tu problema.
Patryk Ćwiek
posible duplicado de las clases Match multiple cases en scala
nawfal

Respuestas:

135

Falta el paréntesis para sus clases de casos. Las clases de casos sin listas de parámetros están en desuso.

Prueba esto:

abstract class MyAbstract
case class MyFirst() extends MyAbstract
case class MySecond() extends MyAbstract

val x: MyAbstract = MyFirst()


x match {
   case aOrB @ (MyFirst() | MySecond()) => doSomething(aOrB)
   case _ => doSomethingElse()
}

Si tiene demasiados parámetros para sus clases de casos y no le gusta tener que escribir Foo(_,_,..)patrones largos , entonces tal vez:

x match {
   case aOrB @ (_:MyFirst | _:MySecond) => doSomething(aOrB)
   case _ => doSomethingElse()
}

O solo:

x match {
   case _:MyFirst | _:MySecond => doSomething(x) // just use x instead of aOrB
   case _ => doSomethingElse(x)
}

¿Pero quizás solo querías objetos de caja singleton?

abstract class MyAbstract
case object MyFirst extends MyAbstract
case object MySecond extends MyAbstract

val x: MyAbstract = MyFirst

x match {
   case aOrB @ (MyFirst | MySecond) => doSomething()
   case _ => doSomethingElse()
}
Faiz
fuente
1
¿Y no hay forma de evitar los paréntesis? Como tengo algunos parámetros y se vuelve feo: case a @ (MyFirst ( , _, _, _, _) | MySecond ( , _, _, _, _)) => doSomething ()
psisoyev
9
¿Te perdiste obj @ (_: MyFirst | _: MySecond)?
Jean-Philippe Pellet
Necesito obj en los casos en los que lo estoy usando en una doSomethingllamada. En mi caso, la llamada de doSomethingno se estaba usando obj, así que no la necesito. Pero de todos modos, ¡gracias por tu comentario!
psisoyev
@ Jean-PhilippePellet De hecho, lo he hecho. Déjame editar mi publicación para agregarla.
Faiz
1
Sería bueno si la compilación fuera lo suficientemente inteligente como para encontrar el tipo común más cercano en lugar de tomar el tipo de entrada por defecto.
nilskp