Can a Scala case object be used in a match case?
E.g. this does not work:
abstract class A
case object B extends A
object something {
val b = B
b match { case _:B => println("success") }
}
not found: type B
b match { case _:B => println("success") }
^
You need to specify B.type:
object something {
val b = B
b match { case _:B.type => println("success") }
}
Oops, seems that this also compiles:
abstract class A
case object B extends A
object something {
val b = B
b match { case B => println("success") }
}
Scala Fiddle: Can a Scala case object be used in a match case
Related
I have a class which contains a sequence of a generic type like:
sealed trait Interface {}
case class Imp1() extends Interface {}
case class Imp2() extends Interface {}
case class Wrapper[+I <: Interface](interface: I) {}
case class WrapperList(list: Seq[Wrapper[Interface]]) {
...
}
In the WrapperList I want to be able to iterate through the Sequence of wrappers and pattern match on each ones type, eg.
def getImp1s(remaining: Seq[Wrapper[Interface]] = list): Seq[Wrapper[Imp1]] = {
if (remaining.length == 0) Seq()
else remaining.head match {
case wrapper: Wrapper[Imp1] => get(remaining.tail) :+ wrapper
case _ => get(remaining.tail)
}
}
As you can probably guess I'm running into
non-variable type argument Playground.Imp1 in type pattern Playground.Wrapper[Playground.Imp1] is unchecked since it is eliminated by erasure
To overcome this I was under the impression I could use TypeTags or ClassTags to preserve the type, something like:
case class Wrapper[+I <: Interface](interface: I)(implicit tag: TypeTag[I]) {}
However this doesn't seem to work, I still get the same warning. Could someone explain how I can match using the TypeTag? I'd rather avoid creating concrete versions of my generic class which extend a generic abstract class, but do understand that this is maybe the easiest solution.
Thanks for your help :)
Try
import shapeless.TypeCase
val `Wrapper[Imp1]` = TypeCase[Wrapper[Imp1]]
def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
if (remaining.isEmpty) Seq()
else remaining.head match {
case `Wrapper[Imp1]`(wrapper) => getImp1s(remaining.tail) :+ wrapper
case _ => getImp1s(remaining.tail)
}
}
getImp1s(Seq(Wrapper(Imp1()), Wrapper(Imp2()), Wrapper(new Interface {})))
// List(Wrapper(Imp1()))
getImp1s(Seq(Wrapper(Imp2()), Wrapper(Imp1()), Wrapper(new Interface {})))
// List(Wrapper(Imp1()))
The same can be achieved without Shapeless with custom extractor
object `Wrapper[Imp1]` {
def unapply(arg: Any): Option[Wrapper[Imp1]] = arg match {
case Wrapper(Imp1()) => Some(Wrapper(Imp1()))
case _ => None
}
}
or directly
def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
if (remaining.isEmpty) Seq()
else remaining.head match {
case Wrapper(Imp1()) => getImp1s(remaining.tail) :+ Wrapper(Imp1())
case _ => getImp1s(remaining.tail)
}
}
or
def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
if (remaining.isEmpty) Seq()
else remaining.head match {
case Wrapper(_: Imp1) => getImp1s(remaining.tail) :+ Wrapper(Imp1())
case _ => getImp1s(remaining.tail)
}
}
I have a type whose shape is like this:
val myType: Future[Either[MyError, TypeA]] = // some value
I know that I could pattern match on this and get to the Right or Left type, but the problem is that I would have to nest my pattern matching logic. I'm looking for much more elegant way of handling this? Any suggestions?
If you encode your MyError as an exception, you don't need the Either anymore and can simply patternMatch against the completion, or use a recoverWith to map it to another type:
myType.onComplete {
case Success(t) =>
case Failure(e) =>
}
To map your existing Either types you could do something like this:
case class MyException(e: MyError) extends Exception
def eitherToException[A](f: Future[Either[MyError,A]]): Future[A] = {
f.flatMap {
case Left(e) => Future.failed(MyException(e))
case Right(x) => Future.successful(x)
}
}
val myType2 = eitherToException(myType)
Alternatively, if MyError and TypeA are under your control, you could create a common super type and pattern match against that:
sealed trait MyResult
final case class MyError() extends MyResult
final case class TypeA() extends MyResult
myType.map {
case MyError() => ...
case TypeA() => ...
}
You can create custom extractor objects:
object FullSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Right(x)) => Some(x)
case _ => None
}
}
object PartSuccess {
def unapply[T](x: Try[Either[MyError, T]]) = x match {
case Success(Left(err)) => Some(err)
case _ => None
}
}
And
val myType: Future[Either[MyError, TypeA]] = // some value
myType.onComplete {
case FullSuccess(x) => ... // equivalent to case Success(Right(x))
case PartSuccess(x) => ... // equivalent to case Success(Left(x))
case Failure(e) => ...
}
Or, in other words:
Can I verify with matching if elements in a tuple are of the same case class, despite having different values in theirs fields (arguments)?
Is there something equivalent to the case[T] below?
sealed abstract class RootClass
case class ChildClassX(valuex: Boolean) extends RootClass
case class ChildClassY(valuey: Boolean) extends RootClass
// and other case classes here...
object Foo {
def compare(a: RootClass, b: RootClass) = {
(a, b) match {
case[T] (T(a), T(b)) => a == b
case _ => throw Exception("a and b should be of same child classes.")
}
}
I hope I dont have to do:
object Foo {
def compare(a: RootClass, b: RootClass) = {
(a, b) match {
case (ChildClassX(a), ChildClassX(b)) | (ChildClassY(a), ChildClassY(b)) | (ChildClassZ(a), ChildClassZ(b)) | etc. => a == b
case _ => throw Exception("a and b should be of same child classes.")
}
}
Related:
matching
The most reasonable solution that I can think of is to simply compare the two items' classes.
(a, b) match {
case (x,y) if x.getClass == y.getClass => "matching classes"
case _ => "no match"
}
I am not aware of any construct that works the way you describe, like case[T].
This would be a solution, I guess - if it's really only about the classes:
object Foo {
def compare[A,B](a: A, b: B) =
if (a.getClass.getSuperclass != b.getClass.getSuperclass)
throw new MatchError("a and b should be of same child classes.")
else (a.getClass == b.getClass)
}
No matching involved... Maybe someone has a more elegant solution? But this is maybe the shortest...
Example test code:
object ObjCmp extends App {
case object X
val p: Product = ChildClassX(true)
println(Foo.compare(ChildClassX(true), ChildClassX(false)))
println(Foo.compare(ChildClassX(true), ChildClassY(false)))
println(Foo.compare(ChildClassX(true), p))
println(Foo.compare(ChildClassX(true), X))
}
prints:
true
false
true
Exception in thread "main" scala.MatchError: a and b should be of same child classes.
I have some code
case class A(s:String) {val x = "hello"}
Why can't I access the static variable x without instantiating the class A? If I type
A.x
I get the following error:
error: value x is not a member of object A
Edit:
I missed out mentioning the remaining code. Here is the example that I would like to use:
abstract class A { val name:String }
case class B(i:Int) extends A { val name = "B" }
case class C(b:Boolean) extends A { val name = "C" }
def getType(s:String) = s match {
case B.name => println ("Object B")
case C.name => println ("Object C")
}
The error:
scala> def getType(s:String) = s match {
| case B.name => println ("Object B")
| case C.name => println ("Object C")
| }
<console>:11: error: value name is not a member of object B
case B.name => println ("Object B")
^
<console>:12: error: value name is not a member of object C
case C.name => println ("Object C")
^
As to why use case classes, the case classes are not defined for this purpose. Elsewhere I have some code like:
def func(a:A) = a match {
case b:B =>
case c:C =>
...
}
Well, you cannot call the "static" variable x, because in Scala there are no static variables. You are declaring x to be a regular member of class A, which you could access if you had an instance of class A.
What you try to do by calling A.x is accessing a value with the name "A". There happens to be such a value in scope - the compiler generated companion object for your case class A.
But this object A has no member "x", therefore the compiler rightly complains about it.
You can add the value x to the object A instead of the class/type A by doing the following:
case class A(s:String)
object A { val x = "hello" }
From the small amount you described of the problem, it sounds like case classes are just not for you.
Alternate patterns include...
Constants:
val Abs1 = "1" //note that it starts with an uppercase letter
val s: String = ...
s match {
case Abs1 => ...
case _ =>
}
Extractors:
object Positive {
def unapply(i: Int): Option[Int] = if(i >= 0) Some(i) else None
}
val i: Int = ...
i match {
case Positive(p) => ... //p will be bound to the matched positive number
case _ => ...
}
Case Classes (used properly):
case class MyType(s: String)
val x: MyType = ...
x match {
case MyType("a") => ...
case MyType("b") => ...
case MyType(matched) => ...
//matched will be bound to whatever string was used to construct the MyType instance
}
Case Objects:
abstract sealed trait Foo { def s: String }
case object Bar extends Foo { val s = "I'm a Bar" }
case object Baz extends Foo { val s = "I'm a Baz" }
val x: Foo = ...
x match {
case Bar => ...
case Baz => ...
//no other possibilities because Foo is sealed
}
Leaving aside issues of design for a moment.
If you need a workaround then you can bind an identifier to a matched case class or case object and use the bound identifier to access members of the matched entity. The bound identifier can be used in guard statements in the match or in the action provided for the match:
case class Bob(x: String, y: String) {val z = "Bragging"}
val bob1 = Bob("Big", "Bad")
bob1 match {
case b # Bob(x, y) if b.z == "Bragging" => println("Got "+x+", "+y+", "+b.z+" Bob!")
case _ =>
}
case object Bob {val z = "Balding"}
val bob2 = Bob
bob2 match {
case b # Bob if b.z == "Balding" => println("Got "+b.z+" Bob!")
case _ =>
}
Returning to design, in your case class definition you declare 'name' in the constructor body of B but you would get more useability from having 'name' as a parameter:
case class B(i: Int, name: String) extends A
Which could then match like this:
def getType(s:String) = s match {
case B(_, name) => println ("Object B("+name+")")
...
Finally it's hard to say without further detail but I suspect that mapping case classes to a large set of similar entities on a one to one basis is perhaps not the best choice, better to use case objects, or instances of a limited number of case classes or even tuples.
I'm doing matching against some case classes and would like to handle two of the cases in the same way. Something like this:
abstract class Foo
case class A extends Foo
case class B(s:String) extends Foo
case class C(s:String) extends Foo
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(sb) | C(sc) => "B"
case _ => "default"
}
}
But when I do this I get the error:
(fragment of test.scala):10: error: illegal variable in pattern alternative
case B(sb) | C(sc) => "B"
I can get it working of I remove the parameters from the definition of B and C but how can I match with the params?
Looks like you don't care about the values of the String parameters, and want to treat B and C the same, so:
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(_) | C(_) => "B"
case _ => "default"
}
}
If you must, must, must extract the parameter and treat them in the same code block, you could:
def matcher(l: Foo): String = {
l match {
case A() => "A"
case bOrC # (B(_) | C(_)) => {
val s = bOrC.asInstanceOf[{def s: String}].s // ugly, ugly
"B(" + s + ")"
}
case _ => "default"
}
}
Though I feel it would be much cleaner to factor that out into a method:
def doB(s: String) = { "B(" + s + ")" }
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(s) => doB(s)
case C(s) => doB(s)
case _ => "default"
}
}
There are a couple of ways that I can see to achieve what you are after, if you have some commonality between case classes. The first is to have the case classes extend a trait which declares the commonality, the second is to use a structural type which removes the need to extend your case classes.
object MuliCase {
abstract class Foo
case object A extends Foo
trait SupportsS {val s: String}
type Stype = Foo {val s: String}
case class B(s:String) extends Foo
case class C(s:String) extends Foo
case class D(s:String) extends Foo with SupportsS
case class E(s:String) extends Foo with SupportsS
def matcher1(l: Foo): String = {
l match {
case A => "A"
case s: Stype => println(s.s); "B"
case _ => "default"
}
}
def matcher2(l: Foo): String = {
l match {
case A => "A"
case s: SupportsS => println(s.s); "B"
case _ => "default"
}
}
def main(args: Array[String]) {
val a = A
val b = B("B's s value")
val c = C("C's s value")
println(matcher1(a))
println(matcher1(b))
println(matcher1(c))
val d = D("D's s value")
val e = E("E's s value")
println(matcher2(d))
println(matcher2(e))
}
}
The structural type method generates a warning about erasure which, at present I'm not sure how to eliminate.
Well, it doesn't really make sense, does it? B and C are mutually exclusive, so either sb or sc get bound, but you don't know which, so you'd need further selection logic to decide which to use (given that they were bound to a Option[String], not a String). So there's nothing gained over this:
l match {
case A() => "A"
case B(sb) => "B(" + sb + ")"
case C(sc) => "C(" + sc + ")"
case _ => "default"
}
Or this:
l match {
case A() => "A"
case _: B => "B"
case _: C => "C"
case _ => "default"
}