Pattern matching or isInstanceOf in Scala - scala

Suppose I have the following class hierarchy:
trait A; class A1 extends A; class A2 extends A
Now I need to filter A1 instances in List[A]. I use either pattern matching or isInstanceOf.
as.filter(cond(_){case _: A1 => true}) // use pattern matching
as.filter(_.isInstanceOf[A1]) // use isInstanceOf
Does it work the same ? What would you prefer ?

Why don't you use collect? That has the added benefit that the returned list will be of the right type (List[A1] instead of List[A])
val a1s = as.collect { case x:A1 => x }

While the accepted answer gives you a good advice, please note that typecase in scala is not different than using isInstanceOf combined with asInstanceOf. This two examples are roughly equivalent:
def foo(x: Any) = x match {
case s: String = println(s"$s is a String)
case _ => println("something else")
}
def foo(x: Any) = x match {
case _ if x.isInstanceOf[String] => println(s${x.asInstanceOf[String]} is a String)
case _ => println("something else")
}
So in your specific example it doesn't really matter which of the two you use: you'll always end up doing some kind of downcasting, which is something to generally avoid.
See how the second version is considerably uglier, hence more appropriate, since you're doing a "ugly" thing in a functional language.
So, I'd go with
val a1s = as.collect{case x if x.isInstanceOf[A1] => x.asInstanceOf[A1]}
Ugly things should look ugly.

Does it work the same?
The same answer will be generated, but different code will be emitted in each case, as you might expect.
You can examine the IL which is generated in each case, as follows. Create a "test.scala" file with the following contents:
import PartialFunction.cond
trait A; class A1 extends A; class A2 extends A
class Filterer {
def filter1(as: List[A]) =
as.filter(cond(_){case _: A1 => true}) // use pattern matching
def filter2(as: List[A]) =
as.filter(_.isInstanceOf[A1]) // use isInstanceOf
}
Then run:
scalac test.scala
To examine the IL for the as.filter(cond(_){case _: A1 => true}) version, do
javap -c 'Filterer$$anonfun$filter1$1'
javap -c 'Filterer$$anonfun$filter1$1$$anonfun$apply$1'
Then to examine the IL for the as.filter(_.isInstanceOf[A1]) version, you can do
javap -c 'Filterer$$anonfun$filter2$1'
The "cond" version uses more intermediate values and instantiates more objects representing the extra anonymous functions involved.

Related

A way to avoid asInstanceOf in Scala

I have this hierarchy of traits and classes in Scala:
trait A
trait B[T] extends A {
def v: T
}
case class C(v:Int) extends B[Int]
case class D(v:String) extends B[String]
val l:List[A] = C(1) :: D("a") :: Nil
l.foreach(t => println(t.asInstanceOf[B[_]].v))
I cannot change the type hierarchy or the type of the list.
Is there a better way to avoid the asInstanceOf[B[_]] statement?
You might try pattern matching.
l.collect{case x :B[_] => println(x.v)}
You might try something like this:
for (x <- l.view; y <- Some(x).collect { case b: B[_] => b }) println(y.v)
It doesn't require any isInstanceOf or asInstanceOf, and never crashes, even if your list contains As that aren't B[_]s. It also doesn't create any lengthy lists as intermediate results, only small short-lived Options.
Not as concise, but also much less surprising solution:
for (x <- l) {
x match {
case b: B[_] => println(b.v)
case _ => /* do nothing */
}
}
If you could change the type of l to List[B[_]], this would be the preferable solution.
I think the most ideomatic way to do it would be to supply B with an extractor object and pattern match for B values:
object B {
def unapply[T](arg: B[T]): Some[T] = Some(arg.v)
}
l.collect{case B(x) => println(x)}
If B is declared in a source file you can't alter you might need a different name for the extractor object.

Re-writing Pattern Matching with For-Comprehension

Given the following types:
sealed trait Pet {
val name: String
}
case class Dog(override val name: String) extends Pet
case class Cat(override val name: String) extends Pet
sealed trait Error
case object DBConnection extends Error
case object NoResults extends Error
We write a function that searches for a pet by its name.
def foo(petName: String): Either[Error, Pet] = {
val results: Either[Error, List[Pet]] = ??? // does not matter
val foundPet: Option[Pet] = results match {
case left # Left(_) => None
case Right(ps) => ps.find(_.name == petName)
}
foundPet match {
case None => Left(NoResults)
case Some(p) => Right(p)
}
}
Please ignore any improvements to the above code with respect to the database call.
Ideally, I'd prefer to write the above code as a simple for comprehension, taking advantage of the Either monad. The pattern matching is easy to read, I believe, but the for alternative would be more concise, I suspect.
How would I re-write the above code with a for-comprehension? I suppose that I could just make methods that match the return type of Either[Error, Pet], but was not sure.
Problem is Scala Either isn't a monad and it's not biased hence you can't use it in a for-comprehension: you have to get a LeftProject or RightProjection first as other poster mentioned.
If you're open for little scalaz. scalaz disjunction (\/) is right biased and follows all monad laws. when you map over it it gives you right value.
so your type will become
val results : \/[Error,List[Pet]]
and results.map will give you List[Pet] because scalaz disjunction is right biased.
this may be helpful as well
You can put the find into the for-comprehension and use toRight to convert it to Either. You also must convert these to RightProjections in order for it to work in the for-comprehension (Either does not have flatMap and map on its own).
def foo(petName: String): Either[Error, Pet] = {
val results: Either[Error, List[Pet]] = ???
for {
pets <- results.right
pet <- pets.find(_.name == petName).toRight(NoResults).right
} yield pet
}
The problem with Scala's Either[+A, +B] type is that you have to do right or left projections to get a monad (respectively right or left biased). On the other hand, scalaz's \/[+A, +B] is monadic by default. To get something really concise, with \/[+A, +B] the solution would look like this:
def foo(petName: String): \/[Error, Pet] = {
val results: \/[Error, List[Pet]] = ???
for {
pets <- results
results <- pets.find(_ == petName) \/> NoResults
} yield results
}
But then again, it's an example where using for {} yield ... isn't necessarily the shortest solution, this flatMap gives the same result:
results.flatMap(
_.find(_.name == petName) \/> NoResults
)
Either.fold is a nice and readable way. And you don't need scalaz to do that. Here is the code snippet:
results.fold(
err => Left(err),
lst => lst.find(_.name == petName).map(Right(_)).getOrElse(Left(NoResults))
)

Match “fallthrough”: executing same piece of code for more than one case class?

I have a function in Scala that matches different case classes, but executes the same code on every match. Is there a possibility to “fallthrough”? Or a other nice way to write the code bellow without code duplication and without defining a function?
symbol match {
case Times(a,b) => //some code using a and b
case Plus(a,b) => //same code as above
case Div(a,b) => //again same code as above
}
This is a pretty similar too the question "Match "fallthrough": executing same piece of code for more than one case?" with the difference that I'm intressted in matching with case classes.
You could write your own extractor that combines the three cases and turns them into a tuple:
object BinOp {
def unapply(op: Op) = op match {
case Times(a, b) => Some(a, b)
case Plus(a, b) => Some(a, b)
case Div(a, b) => Some(a, b)
}
}
symbol match {
case BinOp(a, b) =>
}
No, falltroughs are prohibited in Scala since they are a common source of bugs in other languages. You have two three possibilites:
factor out everything that is identical in a function
try to use less specific matching, i.e., by using wildcards. In your example, this could also mean to introduce a superclass BinaryOperation which gives you the possibility of a more generic match. Note that due to case class inheritance restrictions, you would have to rely on using the fields of this superclass instead of having a super case class.
follow Mirko Stocker's nice suggestions of writing a specific extractor.
I see two possible solutions to your problem
1) unapply
Extending on M. Stocker's answer, you could organize your data like so:
trait Op
trait BinaryOp extends Op {
def a: Int
def b: Int
}
object BinaryOp {
def unapply(op: Op) = op match {
case x: BinaryOp => Some((x.a, x.b))
case _ => None
}
}
case class Times(a: Int, b: Int) extends BinaryOp
case class Plus(a: Int, b: Int) extends BinaryOp
case class Div(a: Int, b: Int) extends BinaryOp
Usage:
symbol match {
case BinaryOp(a, b) => f(a, b)
case _ => //...
}
2) Product
All case classes extends the Product trait
This allows you to do the following matching:
symbol match {
case p: Product if p.productArity == 2 => {
val a = p.productElement(0) //this has type Any, so a cast may be necessary
val b = p.productElement(1)
f(a, b)
}
}
The second case is more generic, but it is also type-unsafe. I recommend the first solution.

Generically rewriting Scala case classes

Is it possible to generically replace arguments in a case class? More specifically, say I wanted a substitute function that received a "find" case class and a "replace" case class (like the left and right sides of a grammar rule) as well as a target case class, and the function would return a new case class with arguments of the find case class replaced with the replace case class? The function could also simply take a case class (Product?) and a function to be applied to all arguments/products of the case class.
Obviously, given a specific case class, I could use unapply and apply -- but what's the best/easiest/etc way to generically (given any case class) write this sort of function?
I'm wondering if there is a good solution using Scala 2.10 reflection features or Iso.hlist from shapeless.
For example, what I really want to be able to do is, given classes like the following...
class Op[T]
case class From(x:Op[Int]) extends Op[Int]
case class To(x:Op[Int]) extends Op[Int]
case class Target(a:Op[Int], b:Op[Int]) extends ...
// and lots of other similar case classes
... have a function that can take an arbitrary case class and return a copy of it with any elements of type From replaced with instances of type To.
If you'll pardon the plug, I think you'll find that the rewriting component of our Kiama language processing library is perfect for this kind of purpose. It provides a very powerful form of strategic programming.
Here is a complete solution that rewrites To's to From's in a tree made from case class instances.
import org.kiama.rewriting.Rewriter
class Op[T]
case class Leaf (i : Int) extends Op[Int]
case class From (x : Op[Int]) extends Op[Int]
case class To (x : Op[Int]) extends Op[Int]
case class Target1 (a : Op[Int], b : Op[Int]) extends Op[Int]
case class Target2 (c : Op[Int]) extends Op[Int]
object Main extends Rewriter {
def main (args : Array[String]) {
val replaceFromsWithTos =
everywhere {
rule {
case From (x) => To (x)
}
}
val t1 = Target1 (From (Leaf (1)), To (Leaf (2)))
val t2 = Target2 (Target1 (From (Leaf (3)), Target2 (From (Leaf (4)))))
println (rewrite (replaceFromsWithTos) (t1))
println (rewrite (replaceFromsWithTos) (t2))
}
}
The output is
Target1(To(Leaf(1)),To(Leaf(2)))
Target2(Target1(To(Leaf(3)),Target2(To(Leaf(4)))))
The idea of the replaceFromsWithTos value is that the rule construct lifts a partial function to be able to operate on any kind of value. In this case the partial function is only defined at From nodes, replacing them with To nodes. The everywhere combinator says "apply my argument to all nodes in the tree, leaving unchanged places where the argument does not apply.
Much more can be done than this kind of simple rewrite. See the main Kiama rewriting documentation for the gory detail, including links to some more examples.
I experimented a bit with shapeless and was able to come up with the following, relatively generic way of converting one case class into another:
import shapeless._ /* shapeless 1.2.3-SNAPSHOT */
case class From(s: String, i: Int)
case class To(s: String, i: Int)
implicit def fromIso = Iso.hlist(From.apply _, From.unapply _)
implicit def toIso = Iso.hlist(To.apply _, To.unapply _)
implicit def convert[A, B, L <: HList]
(a: A)
(implicit srcIso: Iso[A, L],
dstIso: Iso[B, L])
: B =
dstIso.from(srcIso.to(a))
val f1 = From("Hi", 7)
val t1 = convert(f1)(fromIso, toIso)
println("f1 = " + f1) // From("Hi", 7)
println("t1 = " + t1) // To("Hi", 7)
However, I was not able to get the implicits right. Ideally,
val t1: To = f1
would be sufficient, or maybe
val t1 = convert(f1)
Another nice improvement would be to get rid of the need of having to explicitly declare iso-implicits (fromIso, toIso) for each case class.
I don't think you'll really find a better way than just using unapply/apply through pattern matching:
someValue match {
case FindCaseClass(a, b, c) => ReplaceCaseClass(a, b, c)
// . . .
}
You have to write out the rules to associate FindCaseClass with ReplaceCaseClass somehow, and although you might be able to do it a little more succinctly by somehow just using the names, this has the added benefit of also checking the number and types of the case class fields at compile time to make sure everything matches just right.
There is probably some way to do this automatically using the fact that all case classes extend Product, but the fact that productElement(n) returns Any might make it a bit of a pain—I think that's where reflection would have to come in. Here's a little something to get you started:
case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])
val iter = From(5,"x",Nil).productIterator
val f = To.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
// res0: Any = To(5,x,List())
But really, I think you're better off with the pattern-matching version.
Edit: Here is a version with the relavent code refactored into a method:
case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])
type Curryable = { def curried: _ => _ }
def recase(from: Product, to: Curryable) = {
val iter = from.productIterator
val f = to.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
}
recase(From(5,"x",Nil), To)
// res0: Any = To(5,x,List())

When to use isInstanceOf and when to use a match-case-statement (in Scala)?

sealed class A
class B1 extends A
class B2 extends A
Assuming we have a List of objects of class A :
val l: List[A] = List(new B1, new B2, new B1, new B1)
And we want to filter out the elements of the type B1.
Then we need a predicate and could use the following two alternatives:
l.filter(_.isInstanceOf[B1])
Or
l.filter(_ match {case b: B1 => true; case _ => false})
Personally, I like the first approach more, but I often read, one should use the match-case statement more often (for reasons I do not know).
Therefore, the question is: Are there drawbacks of using isInstanceOf instead of the match-case statement ? When should one use which approach (and which approach should be used here and why) ?
You can filter like that:
l.collect{ case x: B1 => x }
That is much more readable, IMO.
There's no problem using isInstanceOf, as long as you don't use asInstanceOf.
Code that uses both is brittle, because checking and casting are separate actions, whereas using matching you have a single action doing both.
There are no difference
cat t.scala:
class A {
def x(o: AnyRef) = o.isInstanceOf[A]
def y(o: AnyRef) = o match {
case s: A => true
case _ => false
}
}
$ scalac -print t.scala
[[syntax trees at end of cleanup]]// Scala source: t.scala
package <empty> {
class A extends java.lang.Object with ScalaObject {
def x(o: java.lang.Object): Boolean = o.$isInstanceOf[A]();
def y(o: java.lang.Object): Boolean = {
<synthetic> val temp1: java.lang.Object = o;
temp1.$isInstanceOf[A]()
};
def this(): A = {
A.super.this();
()
}
}
}
The advantage of match-case is that you don't have to cast the object in case you want to perform operations on it that depend on its narrower type.
In the following snippet, using isInstanceOf seems to be fine since you don't perform such an operation:
if (obj.isInstanceOf[A]) println(obj)
However, if you do the following:
if (obj.isInstanceOf[A]) {
val a = obj.asInstanceOf[A]
println(a.someField) // someField is declared by A
}
then I'd be in favour of using match-case:
obj match {
case a: A => println(a.someField)
case _ =>
}
It is slightly annoying that you have to include the "otherwise"-case, but using collect (as hinted at by om-nom-nom) could help, at least if you work with collections inherit from Seq:
collectionOfObj.collect{ case a: A => a}.foreach(println(_.someField))