2

I'm considering using the new Type Dynamic, but find that an obvious use case is not well implemented. I'm trying to create convenience wrappers for an object-oriented database. It suffers from casting and usablity issues, because it serializes and deserializes objects (it's methods return Object).

First question: The get method of my database deserializes an object of type A. Althought A has a method a() and I may know at a given moment, that I have an A, i can't call a(), since java only sees Object. Will the Dynamic mechanism try for me if the "underlying" object actually has that method, or do I have to deal with that in applyDynamic myself? I tried it out in the REPL, doesn't seem to do that for me. If I do have to check for myself, what is the easiest way (using scala's new reflection or not), to check if that object has method "methodname" and if yes invoke it.

Scared off by this https://stackoverflow.com/a/11056410/703862 I fall back to java's reflection, which does the method invocation part quite easily.

I come up with this:

 scala> import java.lang.reflect.Method
 import java.lang.reflect.Method

 scala> class DynTest3 extends Dynamic {
 | def pee():String = "yuppie"
 |   def execSucced(method: Method, args: Any*):Boolean = return try{method.invoke(args); true} catch { case e: Exception => false }
 | def applyDynamic(methodName : String)(args: Any*){
 |     val succed = classOf[DynTest3].getDeclaredMethods.filter(m => m.getName() == methodName).exists(m => execSucced(m))
 | if (!succed)
 | throw new java.lang.NoSuchMethodException
 | }
 | }
 defined class DynTest3

but:

 scala> val y: Object = new DynTest3
 y: Object = DynTest3@74c74b55

 scala> y.asInstanceOf[Dynamic].pee()
 <console>:11: error: value applyDynamic is not a member of Dynamic
          y.asInstanceOf[Dynamic].pee()

So basically, this doesn't work, even if I cast to Dynamic. Casting to Dynamic already would make the whole thing useless, since I want to save the user from casting. But maybe one could create an implicit conversion any2Dynamic...

Any hints?

Community
  • 1
  • 1
ib84
  • 675
  • 5
  • 16
  • It seems to relate to [a recent question about dynamic](http://stackoverflow.com/questions/11062166/dynamic-method-invocation-with-new-scala-reflection-api/11066485)—where I thought `DynamicProxy` is the thing you are looking for. According to Eugene Burmako, who is designing Scala 2.10's macros, the proxy is currently not available but will probably be available again at some point (hopefully in 2.10 final) – 0__ Jul 03 '12 at 02:02
  • thanks Sciss. The accepted answer on that same page is already helpful. Will check it out, but i'm having some problems with intellij again and the code sample doesn't work in the REPL as is. – ib84 Jul 03 '12 at 07:04
  • The proxy is almost there. Chris Hodapp did an excellent job and recreated the proxy from scratch: https://github.com/scala/scala/pull/805. – Eugene Burmako Jul 03 '12 at 07:21
  • @ib84. If the code sample doesn't work in the REPL, please, let me know, and I will fix your problem. My email is in the profile. – Eugene Burmako Jul 03 '12 at 07:23

1 Answers1

7

Dynamic in Scala is a simple compile-time rewriting scheme. The details are provided in SIP-17: Type Dynamic, and here I will try to explain them in different words.

Unlike in C#, where dynamic introduces the entire infrastructure of metaobjects, binders and callsite caching + implements a mini-C# compiler that services overloading during the runtime, in Scala we decided to go with the simplest thing possible.

I've mentioned compile-time rewriting, now it's time to elaborate. There are several rules, but let's take a look at the most important one.

If we cannot compile foo.bar(arg1 ... argN), and foo has a static type that is a subtype of Dynamic, then rewrite the invocation to foo.applyDynamic("bar")(arg1 .. argN). The rewritten expression will be typechecked as usual, as if it were written manually by the programmer.

In your case of y.asInstanceOf[Dynamic].pee(), the receiver of the call, y.asInstanceOf[Dynamic], is definitely of a static type that subtypes Dynamic, so the rewriting is triggered. The result of rewriting is y.asInstanceOf[Dynamic].applyDynamic("pee")(). However, when Scala tries to typecheck this expression, it fails, because Dynamic is just an empty marker trait, which doesn't define any methods.

To solve the problem, you cast to something that subtypes Dynamic and has the applyDynamic member (or you do not cast, but make your API return not Object, but that something). You can either write this something yourself or use DynamicProxy (subclassed to incorporate your custom logic), which I hope will be included in 2.10.0-M5.

Eugene Burmako
  • 13,028
  • 1
  • 46
  • 59