Dynamic member lookup in Scala

Dynamic member lookup in Scala

Dynamic member lookup in Scala

A lot of dynamic typed languages has function that catch all messages the target object doesn’t have. It gives these languages a lot of power to write DSL.

Scala 2.9 introduced an experimental option that enables this sort of dynamic handling of accesses to types in ways that would otherwise fail static type checking. It was refined and made non-experimental in 2.10, though it is still controlled through flag which is disabled by default.

Dynamic trait

Scala’s Dynamic type allows you to access instance members that aren’t statically defined in a class. It’s a marker trait with no members – it provides a hint to the compiler to rewrite method calls and field accesses to missing members as calls to one of the following methods:

  • selectDynamic – method allows to write field accessors
  • updateDynamic – method allows to write field updates
  • applyDynamic – method allows to call methods with arguments
  • applyDynamicNamed – method allows to call methods with named arguments

To shed more light on how these methods might work see examples below together with overview of dynamic translations that are done under the hood:

dyn.foo                     //dyn.selectDynamic("foo")
dyn.foo = "bar"             //dyn.updateDynamic("foo")("bar")  
dyn.foo("bar")              //dyn.applyDynamic("foo")("bar")  
dyn.foo(name = "bar")       //dyn.applyDynamicNamed("foo")(("name" -> "bar"))
dyn.foo(5, name = "bar")    //dyn.applyDynamicNamed("foo")(("", 5),("name", "bar"))  
dyn.foo("bar") = "rab"      //dyn.applyDynamic("foo").update("bar", "rab")

So what you should do if you want to add dynamic member lookup in your code? It’s simple. You just need to write a class that extends Dynamic trait. Inside this class you can implement any of these four methods.

But that is not all. As I mentioned above it’s disabled feature by default so you need to set the compiler option -language:dynamics or add a import scala.language.dynamics to make the feature work.

And that’s all you need to do to make your statically typed code more dynamic.

Sample implementation

As an example, we might want to build our own Map implementation. Our Map needs to do few things: – store data as key-value, where key is String and value is Generic type, – return default(defined) value for keys that doesn’t exists.

We can do it like this :

class MyMap[T](defaultValue: T) extends Dynamic {

  val values: mutable.Map[String, T] = mutable.Map.empty[String, T]

  def selectDynamic(name: String): T = {
    values.get(name) match {
      case Some(value) => value
      case None => defaultValue
    }
  }

  def updateDynamic(name: String)(value: T): Unit = {
    values(name) = value
  }

}

So now we can use it in this way:

val myKeyStore = new MyMap(150)

myKeyStore.foo           //Int: 150
myKeyStore.foo = 69
myKeyStore.foo           //Int: 69

How it works? I think, the code above is self-explanatory. Every time a message is sent to this object, it will try to find value with the key of a sent message in the included Map. If it doesn’t find any, it will return the value defined as default.

Going further

With Dynamic you can also create your own custom builders…

case class Ingredient(name: String, amount: Int = 1)

case class Drink(ingredients: List[Ingredient])

case class Shaker(ingredients: List[Ingredient], multiplier: Int = 1) extends Dynamic {

    def selectDynamic(name: String) = {
      name match {
        case c if c.matches("with|and") => this
        case double if double.matches("double|twice|two") => copy(multiplier = 2)
        case triple if triple.matches("triple|three") => copy(multiplier = 3)
        case ingredient => copy(ingredients :+ Ingredient(ingredient, multiplier), multiplier = 1)
      }
    }

    def please = Drink(ingredients)

}

object Shaker {
    def apply(): Shaker = {
      new Shaker(List.empty[Ingredient], 1)

Now you can build you own drinks in easy way:

Shaker().gin.triple.vodka.twice.lemon.and.ice.please
//result: Drink(List(Ingredient(gin,1), Ingredient(vodka,3), Ingredient(lemon,2), Ingredient(ice,1)))

Dynamic and implicit conversions

Implicit conversions are applied before missing methods are passed into updateDynamicselectDynamicapplyDynamic or applyDynamicNamedmethod. To prove that, Lets go back to first example.

When we call key that doesn’t exists, the MyMap return the value 150.

myKeyStore.ten  		//Int: 150

Let’s see what happens with implicit conversion in scope. First we define implicit class:

import scala.language.implicitConversions

object DynamicImplicit {

  implicit class Ten(obj: Any) {
    def ten = 10
  }
}

Import it in the file where we use our map, and try again to call method above.

import DynamicImplicit._
  
myKeyStore.ten				//Int: 10

Dynamic and postfixOps

Sadly, it does not work well together. Compiler doesn’t know which method it should apply if you chain methods as in the example above. So, you have to use dot as methods separator.

More

You can read about Dynamic in the SIP17. If you want to read more about “modularization” of Scala 2.10 and feature flags you should look into SIP18

@marioosh

Do you like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.

See also:

Download e-book:

Scalac Case Study Book

Download now

Authors

Mariusz Nosiński

I’m an experienced developer who has acquired a broad knowledge. I’m always ready for new challenges and learning new skills.

Latest Blogposts

17.04.2024 / By  Michał Szajkowski

Mocking Libraries can be your doom

Test Automations

Test automation is great. Nowadays, it’s become a crucial part of basically any software development process. And at the unit test level it is often a necessity to mimic a foreign service or other dependencies you want to isolate from. So in such a case, using a mock library should be an obvious choice that […]

04.04.2024 / By  Aleksander Rainko

Scala 3 Data Transformation Library: ducktape 0.2.0.

Scala 3 Data Transformation Library: Ducktape 2.0

Introduction: Is ducktape still all duct tape under the hood? Or, why are macros so cool that I’m basically rewriting it for the third time? Before I go off talking about the insides of the library, let’s first touch base on what ducktape actually is, its Github page describes it as this: Automatic and customizable […]

28.03.2024 / By  Matylda Kamińska

Scalendar April 2024

scala conferences april 2024

Event-driven Newsletter Another month full of packed events, not only around Scala conferences in April 2024 but also Frontend Development, and Software Architecture—all set to give you a treasure trove of learning and networking opportunities. There’re online and real-world events that you can join in order to meet colleagues and experts from all over the […]

software product development

Need a successful project?

Estimate project