0

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?

Bruno
  • 252
  • 1
  • 8

2 Answers2

1

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
}
Alec
  • 31,829
  • 7
  • 67
  • 114
  • Cheers! Makes perfect sense. Had a feeling that there was some easy and simple solution out there! Thanks – Bruno Jan 13 '17 at 20:21
  • btw, if Foo had multiple params, how would you select the 'bar' without describing them all? – Bruno Jan 13 '17 at 21:15
  • @Bruno Just use underscore patterns for the other params. So something like `Some(Foo(i: Int, _, _))` you you add two more params to `Foo`. – Alec Jan 13 '17 at 21:18
  • that is what I mean with 'without describing them all'. The class that I am working with has a lot of params, and it will not look good if I do that for all of them (will be hard to read and error prone). Was thinking if there was a way to do it similar to: new Foo(bar = " some value"). Where we explicitly select the param – Bruno Jan 13 '17 at 21:22
  • 1
    @Bruno Oh sorry I misunderstood. :). There is a way, but it isn't direct. You end up making your own extractor for just that field. Something like [this](http://stackoverflow.com/a/3475927/3072788). – Alec Jan 13 '17 at 21:24
1

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

zhelezoglo
  • 212
  • 2
  • 11