Dynamic types

Today we bring you some extra black magic. I was reading the other day a little bit about the Scala Improvement Processes (SIPs), I found something that really got my attention: dynamic types.

What are they?

Dynamic types allow to define the behavior of some object when a method or a member that doesn’t exist is tried to be accessed. I.e.:

class MyClass {
  def someMethod(): String = "hi"
}
val myInstance = new MyClass
myInstance.nonExistentMethod

will complain at compile time. With dynamic types we can decide that, in case some whatever-its-name-is method is invoked, it will return certain value.

How do they work?

In order to use dynamic types, it’s necessary to import scala.language.dynamics and extend trait Dynamic:

class MyClass extends Dynamic {
  //...
}

The class (or trait) that we will define may have all the attributes and methods we want, but we can also define the following:

applyDynamic

this method is in charge of managing the invocations to the instance methods.
If we want our instance to know how to invoke ‘myMethod’ method with a ‘hi’ parameter, we can do it as follows:

class MyClass extends Dynamic {
  def applyDynamic(methodName: String)(args: Any*) = {
    val beginning = 
      args.headOption.getOrElse("hello")
    if (methodName=="myMethod") 
      println(beginning + " you")
    else println("Dunno...")
  }
}

If we now create a new instance and we invoke another made-up method, we’ll see that it will print out that it doesn’t know what you’re talking about:

val myInstance = new MyClass
myInstance.anotherMethod()//Dunno...

And if we invoke the myMethod method we’ve considered:

myInstance.myMethod("hi")//hi you
myInstance.myMethod(2)//2 you
myInstance.myMethod()//hello you

applyDynamicNamed

It works exactly like the applyDynamic method, except that it only allows to get by name the parameters that are used when invoking the method:

class MyClass extends Dynamic {
  def applyDynamicNamed(name: String)(args: (String, Any)*) = {
    val (argName,argValue) = 
      args.headOption.getOrElse(("unknown","hello"))
    if (name=="myMethod") 
      println(s"$argName : $argValue - you")
    else println("Dunno...")
  }
}

If the method is invoked without explicitly naming any parameter, this parameter’s name will be an empty string. The thing is, to make applyDynamicNamed work, at least one of the parameters has to owe a name.

myInstance.myMethod(test=2)//test : 2 - you
myInstance.myMethod(true,test=2)// : true - you
myInstance.myMethod(true)
/*
error: value applyDynamic is not a member of MyClass
error after rewriting to myInstance.<applyDynamic: error>("myMethod")
possible cause: maybe a wrong Dynamic method signature?
*/

selectDynamic

It is similar to a ‘dynamic getter’. I mean:

class MyClass extends Dynamic {
  def selectDynamic(name: String) = 
    s"My $name equals this string"
}
val myInstance = new MyClass
myInstance.someAttribute//My someAttribute equals this string

updateDynamic

And with this last method, we define the updating behavior of all members of the instance that are not statically defined:

class MyClass extends Dynamic {
  def updateDynamic(name: String)(value: Any) {
    println(s"you updated your attribute $name with $value value")
  }
}
val myInstance = new MyClass
myInstance.myAttribute = 2
//you updated your attribute myAttribute with 2 value

NB: It doesn’t work really well in the REPL (actually it is compiled as a getter). If you encapsulate it inside an App block, it should work.

Easy peasy.

Why this in a statically typed language?

haters

Official motivation of this SIP was:

Static types are great, but sometimes we would like to do without them. This could give better support for flexible DSLs that do not require defining the members of a type. It could also provide simpler interfaces with dynamic languages. Scala already provides a number of ways to escape from the straightjacket of the type system: The universal type Any, type tests and type casts, as well as pattern matching. However, one thing is missing: When selecting a member qual.sel, sel must be defined in qual, or be addable with an implicit conversion. This means that we can select members or call methods only if it is statically known that the receiver type supports that member.

Even it’s true that the purists may see it like an aberration of the statically typed language nature; for the jugglers, it could be seen as another way to risk their necks defining DSLs without compile-time checkings. You judge

Peace out!

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s