Bruno
Bruno

Reputation: 250

Scala : Pattern matching with Option[Foo] and parameter of Foo

How can rewrite the following to make it more 'Scala way' or use just one match?

case class Foo(bar: Any)
val fooOpt = Some(Foo("bar as String"))

def isValid(p: Any) = p match {
   case _ @ (_: String | _: Int) => true
   case _ => false
}

//Is it possible to check for the type of bar directly in this if statement?
fooOpt match {
    case Some(f) if isValid(f.bar) => doSomething
    case _ => doSomethingElse
}

One alternative would be using the isInstanceOf.

fooOpt match {
     case Some(f) if f.bar.isInstanceOf[String] => doSomething
     case Some(f) if f.bar.isInstanceOf[Int] => doSomething //could also rewrite to use just one case
     case _ => doSomethingElse
}

Is there other way?

Upvotes: 0

Views: 207

Answers (2)

zhelezoglo
zhelezoglo

Reputation: 212

Is there other way?

Although the solution with one big patten match works(and can be used if you really can't change bar to anything more specific than Any), it is not a proper 'Scala way' of dealing with this situations in general if you have control over Foo.

A better way would be to make Foo generic:

case class Foo[T](bar: T)

And have either a generic doSomething, if it can work with any particular T:

def doSomething[T](foo: Foo[T]): SomeType = ???

or to have different versions of it for different possible T's you have, if it should react on them differently:

def doSomethingWithString(foo: Foo[String]): SomeType = ???
def doSomethingWithInt(foo: Foo[Int]): SomeType = ???

Then you can use it just like this:

val fooOpt = Some(Foo("bar as String"))
fooOpt.map(doSomething).orElse(doSomethingElse)

or like this:

val fooOptString = Some(Foo("bar as String"))
fooOptString.map(doSomethingWithString).orElse(doSomethingElse)

val fooOptInt = Some(Foo(1))
fooOptInt.map(doSomethingWithInt).orElse(doSomethingElse)

So, in this case compiler checks types for you, answering to:

Is it possible to check for the type of bar directly?

And in many situations you can avoid using pattern match at all, using methods like map, orElse, etc. with proper typing. This might be an answer for this:

could also rewrite to use just one case

Upvotes: 1

Alec
Alec

Reputation: 32309

This can all be done in one big pattern match:

fooOpt match {
   case Some(Foo(_: Int | _: String)) => doSomething
   case _ => doSomethingElse
}

If you want to get the Int or String out, just split that case:

fooOpt match {
   case Some(Foo(i: Int)) => doSomething
   case Some(Foo(s: String)) => doSomething
   case _ => doSomethingElse
}

Upvotes: 1

Related Questions