Renaud
Renaud

Reputation: 16501

Does Scala optimize this code?

In the code below, does it matter where I put the query object (within the loop, or outside)? For readability, I would prefer this 1st version:

class MyClass {
  db withSession {
    names.foreach { name =>
      val query = MyEntity.createFinderBy(_.name)  <----------
      query.list(text).foreach(res =>
        doSomething
      }
    }
  }
}

But isn't this 2nd version better?

class MyClass {
  db withSession {
    val query = MyEntity.createFinderBy(_.name)  <----------
    names.foreach { name =>
      query.list(text).foreach(res =>
        doSomething
      }
    }
  }
}

Or even?

class MyClass {
  val query = MyEntity.createFinderBy(_.name)  <----------
  db withSession {
    names.foreach { name =>
      query.list(text).foreach(res =>
        doSomething
      }
    }
  }
}

In Java, I would put it in a static final field at the top of the class...

Upvotes: 1

Views: 212

Answers (2)

kiritsuku
kiritsuku

Reputation: 53348

scalac does not (can not) optimize your code because it doesn't use an effect system. This makes it almost impossible for scalac to care if an optimization can break code. For example, if MyEntity.createFinderBy(_.name) is not pure (maybe it increments a counter which counts the number of accesses) it will make a change if it is executed once or in each iteration.

In such cases I can suggest to change the position of the function literal:

scala> 1 to 3 foreach {x => println("x"); println(x)}
x
1
x
2
x
3

scala> 1 to 3 foreach {println("x"); x => println(x)}
x
1
2
3

In the second example a block returning a function passed to foreach is created (after executing other expressions), whereas in the former one, the whole block is a single function.

Upvotes: 2

Luigi Plinge
Luigi Plinge

Reputation: 51109

No it does not, because in the first example you specifically told it to call createFinderBy (and also list) for each element. There's no guarantee that those methods will return the same value each time (i.e. are referentially transparent), so the values can't be memoized or the code optimized to the second or third examples.

It would be a nice feature if you could annotate methods as such, or if the compiler could work it out, but at the moment it doesn't.

As an aside, you could change the createFinderBy method so that it stores values in a mutable.Map cache and returns values using the getOrElseUpdate method, which returns a cached value if your query is already in the map, or else calculates the query and caches it.

Upvotes: 2

Related Questions