Structural types

In computer programming, Ducktyping is the definition of types given by the attributes and methods that compose them, and not by inheritance. For instance, in Scala we can define the following structural type:

type Animal = {
  val legs: Int
  def noise(): String
}

def animalDescription(a: Animal): String =
  s"My animal has ${a.legs} legs and says ${a.noise()}"

In this case, we can say that every class that has an integer attribute called legs and a method noise that returns a String can be referred to as Animal.

Then now, what’s the difference between this and classic inheritance?

trait Animal {
  val legs: Int
  def noise(): String
}
class Dog extends Animal{
  val legs = 4
  def noise() = "woof"
}

It may seem that there’s none a priori. In fact, we have to take into account that in Scala, when we use structural types we experience some compile-time overhead because it has to be inferred if the type at issue meets the restrictions that the structural type imposes.

Then what the hell do structural types offer?

Quite simple: up until now, we have been assuming that our implementation is 100% home-made. But, what if we are using a framework or a third-party library and the authors are so nice that they declare classes as final or define sealed traits (even private at certain scope)?

tGWoYYo

Well, apart from them having every right to do so, we wouldn’t be able to do anything like this:

package some.private.library {

  sealed trait TraitIWantToExtend {
    val member1: Int
    def method2(): Boolean
  }

  private[some.private] class ClassIMaybeWantToMock extends TraitIWantToExtend {
    val member1 = 2
    def method2() = true
  }

}

package my.package {

  class MyClass extends TraitIWantToExtend {// nuke-explosion in scalac!
    val member1 = 3
    def method2() = false
  }

  def methodToRun[T<:TraitIWantToExtend](obj: T) {
    println(obj.member1)
    println(obj.method2())
  }

}

However, if we apply everything we have learnt until now, we can extract the core of the features we are interested in and that are common between our class and the class defined in the third party library:

package my.package {

  type CommonFeatures = {
    val member1: Int
    def method2(): Boolean
  }

  class MyClass {// no nuke explosion now ^^
    val member1 = 3
    def method2() = false
  }

  def methodToRun[T<:CommonFeatures](obj: T) {
    println(obj.member1)
    println(obj.method2())
  }

}

So, just as with about everything in life, we do not recommend the abuse of this feature. Nevertheless, when facing situations similar to the ones exposed above, it can be really useful.

Peace out friends!

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