Question

If function accepts structural type, it can be defined as:

def doTheThings(duck: { def walk; def quack }) { duck.quack }

or

type DuckType = { def walk; def quack  }
def doTheThings(duck: DuckType) { duck.quack }

Then, you can use that function in following way:

class Dog {
    def walk { println("Dog walk") }
    def quack { println("Dog quacks") }
}

def main(args: Array[String]) {
    doTheThings(new Dog);
}

If you decompile (to Java) the classes generated by scalac for my example, you can see that argument of doTheThings is of type Object and the implementation uses reflection to call methods on the argument (i.e.duck.quack)

My question is why reflection? Isn't it possible just to use anonymous and invokevirtual instead of reflection?

Here is way to translate(implement) the structural type calls for my example (Java syntax, but the point is the bytecode):

class DuckyDogTest {
  interface DuckType {
    void walk();
    void quack();
  }

  static void doTheThing(DuckType d) {
    d.quack();
  }

  static class Dog {
    public void walk() { System.out.println("Dog walk"); }
    public void quack() { System.out.println("Dog quack"); }
  }

  public static void main(String[] args) {
    final Dog d = new Dog();
    doTheThing(new DuckType() {
      public final void walk() { d.walk(); }
      public final void quack() { d.quack();}
    });
  }
}
Was it helpful?

Solution

Consider a simple proposition:

type T = { def quack(): Unit; def walk(): Unit }
def f(a: T, b: T) = 
  if (a eq b) println("They are the same duck!")
  else        println("Different ducks")

f(x, x) // x is a duck

It would print Different ducks under your proposal. You could further refine it, but you just cannot keep referential equality intact using a proxy.

A possible solution would be to use the type class pattern, but that would require passing another parameter (even if implicit). Still, it's faster. But that's mostly because of the lameness of Java's reflection speed. Hopefully, method handles will get around the speed problem. Unfortunately, Scala is not scheduled to give up on Java 5, 6 and 7 (which do not have method handles) for some time...

OTHER TIPS

In addition to your proxy object implementing methods on the structural type, it would also need to have appropriate pass-through implementations of all of the methods on Any (equals, hashCode, toString, isInstanceOf, asInstanceOf) and AnyRef(getClass, wait, notify, notifyAll, and synchronized). While some of these would be straightforward, some would be almost impossible to get right. In particular, all of the methods listed are "final" on AnyRef (for Java compatability and security) and so couldn't be properly implemented by your proxy object.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top