5

Is it possible to define a value (in a if) in a for comprehension in Scala for use in yield.

I want to do this to avoid a potential expensive evaluation two times.

An example to illustrate.

for {
 bar <- bars if expensive(bar) > 5
} yield (bar, expensive(bar))

2 Answers2

6

How about this:

for {
 bar <- bars
 exp = expensive(bar)
 if exp > 5
} yield (bar, exp)
Don
  • 555
  • 3
  • 12
  • This only works if `expensive(bar)` type respect for-comprehension's interface (map, flatMap, filter, withFilter etc). The author of the question didn't suggest it does. And if it does, it will return a different result than the code provided by the author. – pedrofurla Nov 09 '12 at 19:41
  • 1
    @pedrofurla OK. expensive() might not return collection. So change "<-" to "=" – Don Nov 09 '12 at 19:45
  • Hm... didn't know you could omit the `val` there. Nice. Still, `<-` and `=` have completely different meanings, even if it returns a collection. BTW, collection is not necessary, only the 'arguably monadic' interface. – pedrofurla Nov 09 '12 at 20:52
  • What does this get desugared down to? – Robin Green Nov 10 '12 at 14:31
  • @Robin Is this what you are looking for? bars.filter( p => expensive(p) > 5) map (p=> (p, expensive(p)) ) there is also a discussion about desugar for-comprehension: http://stackoverflow.com/questions/9891407/getting-the-desugared-part-of-a-scala-for-comprehension-expression – Don Nov 10 '12 at 21:02
  • @Don Thanks - using a tip from that link, I have had a look at the real desugaring, which is considerably more complicated, and while it's not as efficient as the code I would write, it does indeed fulfill the condition stated in the question (the expensive function must not be called twice per iteration). – Robin Green Nov 11 '12 at 16:47
3

Yes, you can:

scala> List(1,2,3,4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)

scala> for(n <- res0; val b = n % 2; if b==1) yield b
res2: List[Int] = List(1, 1, 1)
pedrofurla
  • 12,763
  • 1
  • 38
  • 49