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

Tipos dinámicos

Hoy traemos un poco más de magia negra. Leyendo el otro día un poco sobre los procesos de mejora de Scala (SIPs) me encontré algo que realmente me llamó la atención: tipos dinámicos.

¿Qué son?

Los tipos dinámicos permiten definir el comportamiento de un objeto cuando se invoca un método o se intenta acceder a un miembro que no existe. Por ejemplo:

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

dará un fallo en tiempo de compilación. Con los tipos dinámicos podemos decir que en caso de que se invoque a un método, se llame como se llame, devuelva cierto valor.

¿Cómo funcionan?

Para poder utilizar los tipos dinámicos, es preciso importar import scala.language.dynamics y extender del trait Dynamic:

class MyClass extends Dynamic {
  //...
}

La clase (o trait) que definamos puede tener todos los atributos y métodos que queramos, pero además podemos implementar los siguientes métodos:

applyDynamic

este método se encarga de gestionar las llamadas a métodos de la instancia.
Si queremos que nuestra instancia sepa como invocar el método ‘myMethod’ con el argumento “hi”, podemos hacerlo como sigue:

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...")
  }
}

Si ahora creamos una instancia e invocamos otro método inventado, veremos que imprimirá por pantalla que no sabe a que se refiere:

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

Y si invocamos al método myMethod que tenemos contemplado:

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

applyDynamicNamed

Funciona exactamente igual que el applyDynamic solo que permite obtener por nombre los argumentos con los que se invoca al método:

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...")
  }
}

Si se invoca al método sin indicar el nombre de algún parámetro, este será cadena vacía, pero para que se invoque a applyDynamicNamed al menos uno de los argumentos debe tener nombre:

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

Sería el equivalente a un getter dinámico. Es decir:

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

Y con este método definimos el comportamiento de actualización de miembros que no están definidos de manera estática:

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: En la REPL no funciona de manera muy católica (lo interpreta como un getter). Si lo encapsulais dentro de un App debería funcionar.

Easy peasy.

¿Por qué en un lenguaje de tipado estático?

haters

La motivación oficial de la propuesta de mejora es:

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.

Si bien es cierto que los más puristas pueden verlo como una aberración hacia la naturaleza de tipado estático del lenguaje, para los más malabaristas puede dar margen de maniobra a la hora de jugarse el tipo definiendo DSLs sin comprobaciones en tiempo de compilación. Juzguen ustedes.

¡Agur de limón!