29

Is it possible in scala to specialize on the conditions inside an if within a for comprehension? I'm thinking along the lines of:

val collection: SomeGenericCollection[Int] = ...

trait CollectionFilter
case object Even extends CollectionFilter
case object Odd extends CollectionFilter

val evenColl = for { i <- collection if(Even) } yield i
//evenColl would be a SomeGenericEvenCollection instance

val oddColl = for { i <- collection if(Odd) } yield i
//oddColl would be a SomeGenericOddCollection instance

The gist is that by yielding i, I get a new collection of a potentially different type (hence me referring to it as "specialization")- as opposed to just a filtered-down version of the same GenericCollection type.

The reason I ask is that I saw something that I couldn't figure out (an example can be found on line 33 of this ScalaQuery example. What it does is create a query for a database (i.e. SELECT ... FROM ... WHERE ...), where I would have expected it to iterate over the results of said query.

Dylan
  • 13,645
  • 3
  • 40
  • 67
  • This question isn't making much sense. I'm going to answer what I think is being asked, but do try to improve it. – Daniel C. Sobral Oct 31 '11 at 03:53
  • ['For expressions'](http://www.artima.com/pins1ed/for-expressions-revisited.html#23.1) should help. – 4e6 Oct 31 '11 at 03:54
  • 1
    I added a little bit to try to clarify the question, though you seem to be correct in your thinking about what I was asking. – Dylan Oct 31 '11 at 05:03

2 Answers2

60

So, I think you are asking if it is possible for the if statement in a for-comprehension to change the result type. The answer is "yes, but...".

First, understand how for-comprehensions are expanded. There are questions here on Stack Overflow discussing it, and there are parameters you can pass to the compiler so it will show you what's going on.

Anyway, this code:

val evenColl = for { i <- collection if(Even) } yield i

Is translated as:

val evenColl = collection.withFilter(i => Even).map(i => i)

So, if the withFilter method changes the collection type, it will do what you want -- in this simple case. On more complex cases, that alone won't work:

for {
  x <- xs
  y <- ys
  if cond
} yield (x, y)

is translated as

xs.flatMap(ys.withFilter(y => cond).map(y => (x, y)))

In which case flatMap is deciding what type will be returned. If it takes the cue from what result was returned, then it can work.

Now, on Scala Collections, withFilter doesn't change the type of the collection. You could write your own classes that would do that, however.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
3

yes you can - please refer to this tutorial for an easy example. The scala query example you cited is also iterating on the collection, it is then using that data to build the query.

aishwarya
  • 1,970
  • 1
  • 14
  • 22