Scala 2.10: class OhMy extends Dynamic !

Posted by Konrad 'ktoso' Malawski on 14/05/2012 – 01:02;

The first part of the post is a bit like “someone is wrong on the internet!” (yeah, a rant), so feel free to skip it and dive into a detailed post about Scala’s new Dynamic type in part 2 of this post.

Part 1: The rant about a wrong example

I was meaning to write about upcoming Scala features but got sidetracked by work and GeeCON related stuff this week. I’ve just read a post about Dynamic on scala.net.pl and… It’s totally wrong! :< C’mon people – you’ve got scala _as_ your domain name, so at least run the examples in a REPL before you post something ;-)

// example form above mentioned site…
  1. val d = new Dynamic {}
  2. d.bar = "bar" // THIS IS WRONG

Ok, so on the REPL – the first line will compile. Dynamic is in fact a new Trait in Scala 2.10 and you can create new instances of Traits (just as you could in Java with anonymous inner classes) using the new Trait {} syntax. Line 2 though, it utterly wrong. It will cause 2 compile time errors, namely:

# fails will follow
  1. scala&gt; d.bar = "bar"
  2.  
  3. console :11: error: value selectDynamic is not a member of Dynamic
  4. val $ires1 = d.bar
  5.              ^
  6. console: 8: error: value updateDynamic is not a member of Dynamic
  7.        d.bar = "bar"

Dynamic is NOT “a magic type you can set random stuff on”, it’s a bit more discrete. You can build such construct using Dynamic – sure, but that’s not all it is.

Part 2: A deep dive into Scala’s Dynamic type

Ok, end of rant and back to the basics. So… How do we use Dynamic? In fact, it’s used by implementing a few “magic” methods:

  • applyDynamic
  • applyDynamicNamed
  • selectDynamic
  • updateDynamic

Let’s take a look (with examples, at each of them. We’ll start with  the most “typical one”, and move on to those which would allow the construct shown above (which didn’t (back then) compile) and make it work this time ;-)

applyDynamic

Ok, our first magic method looks like this:

// applyDynamic example
  1. object OhMy extends Dynamic {
  2.   def applyDynamic(methodName: String)(args: Any*) {
  3.     println(s"""|  methodName: $methodName,
  4.                 |args: ${args.mkString(",")}""".stripMargin)
  5.   }
  6. }
  7.  
  8. OhMy.dynamicMethod("with", "some", 1337)

So the signature of applyDynamic takes the method name and it’s arguments. So obviously we’d have to access them by their order. Very nice for building up some strings etc. Our implementation will only print what we want to know about the method being called. Did it really get the values/method name we would exect? You can try it out (it’s copy paste ready) in your Scala 2.10.M3 REPL – if you don’t have one at hand: yeah, the output would be:

  methodName: dynamicMethod,
  args: with,some,1337

By the way, did you know about RichString’s stripMargin? It’s a nice way to still have nicely printable strings when you’re using multiline strings. By default it trims everything that is before the | sign (from the left side, for each line). The sign can be overriden of course if you fancy $ signs for example… ;-)

applyDynamicNamed

Ok, that was easy. But it didn’t give us too much control over the names of the parameters.
Wouldn’t it be nice if we could just write JSON.node(nickname = “ktoso”)? Well… turns out we can!

// applyDynamicNamed example
  1. object JSON extends Dynamic {
  2.   def applyDynamicNamed(name: String)(args: (String, Any)*) {
  3.     println(s"""Creating a $name, for:\n "${args.head._1}": "${args.head._2}" """)
  4.   }
  5. }
  6.  
  7. JSON.node(nickname = "ktoso")

So this time instead of just a list of values, we also get their names. Thanks to this the response for this example will be:

Creating a node, for:
 "nickname": "ktoso"

I can easily imagine some pretty slick DLSs being built around this!

selectDynamic

Not it’s time for the more “unusual” methods. apply methods we’re pretty easy to understand. It’s just a method with some arbitrary name. But hey, isn’t almost everything in scala a method – or we can have a method on an object that would act as a field? Yeah, so let’s give it a try! We’ll use the example with applyDynamic here, and try to act like it has a method without ():

// compilation failure
  1. OhMy.name // fails

Hey! Why didn’t this work with applyDynamic? Yeah, you figured it out already I guess. Such methods (without ()) are treated special, as they would usualy represent fields for example. applyDynamic won’t trigger on such calls. Let’s add selectDynamic to the mix then, shall we?

// selectDynamic example
  1. object HasStuff extends Dynamic {
  2.   def selectDynamic(name: String): String = s"I have $name!"
  3. }

And this time when we execute HasStuff.bananas we’ll get “I have bananas!” as expected. Notice that here we return a value instead of printing it. It’s because it “acts as a field” this time around. But we could also return things (of arbitrary types) from any other method described here (applyDynamic could return the string instead of printing it).

updateDynamic

What’s left you ask? Ask yourself the following question then: “Since I can act like a Dynamic object has some value in some field… What else should I be able to do with it?” The answer is obviously: “set it”! That’s what updateDynamic is used for. There is one special rule about updateDynamic though – it’s only valid if you also took care about selectDynamic – that’s why in the first example the code generated errors about both – select and update. For example if we’d implement only updateDynamic, we would get an error that selectDynamic was not implemented and it wouldn’t compile anyway. It makes sense in terms of plain semantics if you think about it.

When we’re done with this example, we can actually make the (wrong) code from the first code snippet work. The bellow snippet will be an implementation of what was shown on the first snippet on that other website, and this time it’ll actually work ;-)

// updateDynamic example
  1. object MagicBox extends Dynamic {
  2.   private var box = mutable.Map[String, Any]()
  3.  
  4.   def updateDynamic(name: String)(value: Any) { box(name) = value }
  5.   def selectDynamic(name: String) = box(name)
  6. }

Using this DynamicMagicBox” we can store items at arbitrary “fields” (well, they do seem like fields, even though they are not ;-)). An example run might look like:

scala> MagicBox.banana = "banana"
MagicBox.banana: Any = banana

scala> MagicBox.banana
res7: Any = banana

scala> MagicBox.unknown
java.util.NoSuchElementException: key not found: unknown

By the way… are you curious how Dynamic [source code] is implemented? The fun part here is that the trait Dynamic, does absolutely nothing by itself – it’s “empty”, just a marker interface, like in the good ol’ Serializable days ;-) Obviously all the heavylifting is done by the compiler here.

Anyway, thanks for reading and… don’t overuse Dynamic, will ya? :-)

Tags: , , , , ,

This post is under “coding, english, lang, scala” and has 4 respond so far.

4 Responds so far- Add one»

  1. 1. Martin Said:

    Nice post! Thank you!

    This feature is available since Scala 2.9.0. You just need to run ‘scala -Xexperimental’

  2. 2. missingfaktor Said:

    Helpful post. But you might want to use a different color scheme. This one is ultra painful to read.

  3. 3. Ktoso Said:

    Lol, right – thanks for pointing it out.

    The white-on-white is an error in fact (I had to restore the blog from backups recently). I’ll fix it asap :)

Post a reply