1

This code returns true as expected:

class MyList extends Traversable[Int] {
  def foreach[U](f: Int => U) = {
    f(1)
    f(2)
    f(3)
  }
}
object Test extends App {
  println(new MyList().exists(_ == 2))
}

MyList only needs to define foreach in order to implement a Traversable. So, somewhere the exists function calls MyList.foreach. Setting a breakpoint at f(1), I see that foreach is called by TraversableLike line 352 for (x <- this). So, is x <- this calling foreach? How? I don't see the call to foreach anywhere.

  349: def exists(p: A => Boolean): Boolean = {
  350:   var result = false
  351:   breakable {
  352:     for (x <- this)
  353:       if (p(x)) { result = true; break }
  354:   }
  355:   result
  366: }
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
David Portabella
  • 12,390
  • 27
  • 101
  • 182

2 Answers2

1

The Scala specification regarding For Comprehension and For Loops talks about this exactly:

The precise meaning of generators and guards is defined by translation to invocations of four methods: map, withFilter, flatMap, and foreach. These methods can be implemented in different ways for different carrier types.

So the translation is effectively:

def exists(p: A => Boolean): Boolean = {
  var result = false
  breakable {
    this.foreach(x => if (p(x)) { result = true; break })
  }
  result
}

And that is why you're see your foreach being invoked.

For different for comprehensions you may see different translations, which are all specified in the specification.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
1

Yuval is correct. But in general, I find referring to the link he gives becomes a bit tiresome. Especially when I am deep inside a for loop I don't understand.

I've developed a macro that helps me figure out how things get de-sugared. It's pretty handy and works from the scala REPL. Just open up a session, and paste in

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

// inspect how scala has desugared your code
def traceSugar[T](t: T): T = macro impl
def impl(c: Context)(t: c.Tree): c.Tree = { println(t); t }

Then, if you ever want to see how something de-sugars, just wrap it in traceSugar. It will print the de-sugared version and evaluate the expression. Very handy for the hairier for loops. In your case,

scala> traceSugar {
 |   var result = false
 |   breakable {
 |     for (x <- Seq(1,2,3))
 |       if (x > 1) { result = true; break }
 |   }
 |   result
 | } 

Results in

{
  var result: Boolean = false;
  scala.util.control.Breaks.breakable(collection.this.Seq.apply[Int](1, 2, 3).foreach[Unit](((x: Int) => if (x.>(1))
    {
      result = true;
      scala.util.control.Breaks.break()
    }
  else
    ())));
  result
}
res8: Boolean = true

You see the foreach, as well as how breakable works.

Alec
  • 31,829
  • 7
  • 67
  • 114