Scala: binding time

224 views Asked by At

In the Strategy pattern, implemented like this:

object StrategyPattern {

  def add(a: Int, b: Int) = a + b
  def subtract(a: Int, b: Int) = a - b
  def multiply(a: Int, b: Int) = a * b

  def execute(callback:(Int, Int) => Int, x: Int, y: Int) = callback(x, y)

def main(args: Array[String]) {
  println("Add:      " + execute(add, 3, 4))
  println("Subtract: " + execute(subtract, 3, 4))
  println("Multiply: " + execute(multiply, 3, 4))
  }
}

I wanted to know (and find how to understand also the other cases, if there is a good reference for the types/forms of the binding times) if the binding time of methods add, substract, and multiply is "construction time" (if I can say so), or at runtime?

1

There are 1 answers

3
Aaron Novstrup On BEST ANSWER

The simple answer is that (for concrete classes) method definitions are bound to method names at compile time, just as they are in Java. Your add method, for example, is completely equivalent to this Java definition:

public int add(int a, int b) {
    return a + b;
}

Non-final methods

If you're analyzing the binding time of a non-final method from the perspective of a call-site where the concrete class is not known statically, then the method name would be considered to have runtime binding to its implementation (due to subclassing/overriding).

Dynamic Classloading

The simple answer is close to the truth, but dynamic classloading in the JVM complicates matters a bit. Because of dynamic classloading, method definitions are technically bound to fully qualified names (e.g., my.pkg.StrategyPattern.add) at runtime. It's certainly possible to have alternative implementations of a my.package.StrategyPattern module and to choose among them dynamically (by loading the corresponding class file(s)).

Of course, this distinction is only relevant to code outside of the compilation unit containing the StrategyPattern definition. Within the compilation unit, methods would always be considered bound at compile time.

Strategy

Since you're asking about the strategy pattern, I guess you have something else in mind? If you're asking whether you can select among the "strategies" at runtime, you can:

val op: (Int, Int) => Int = 
  if (args(0) == "+") add
  else if (args(0) == "-") subtract
  else multiply
execute(op, 3, 4)

In this case, op is bound to a "strategy" function at runtime, but add, subtract, and multiply are still bound to their definitions at compile time.

It's an implementation detail that each of these methods is also associated with an anonymous Function2 class at compile time, and that the appropriate class is instantiated at runtime based on the outcome of the conditional expressions. This detail really isn't relevant to the binding time analysis, since the meaning of the add, subtract, and multiply identifiers is fixed at compile time.