Something I sadly miss in statically typed languages – such as Java – is duck typing. Because it is incredible useful, especially for cross cutting concerns and situations, where you want to interact with library classes in a way not foreseen by their authors.

Of course, if you’re are willing to sacrifice the type checking during compile time, you could always use reflection (or something like dynamic member lookup in C# 4, which is essentially the same).

If you don’t want to do that, you would need a way to give the compiler a hint, what you expect from a duck (probably a method void quack()). You would need to define a type by its method signatures: This is called a Structural Type.

Structural types have some interesting advantages:

  • They shift the focus from abstractions to the actual implementation, since you can introduce any number of interfaces afterwards without touching the code.
  • Decoupling of interface declaration and implementation (physically and temporally). Even for legacy library classes new interfaces can be introduced.
  • A type (interface) can be seen as a View. A concept we are used to from relational databases, where we also almost always define the concrete data objects (tables) first.
  • Think of WebServices: It would be quite handy to provide multiple interfaces for the same service without changing anything in the existing service code.

Unfortunately almost no programming language implements such a type system. Googles Go language is one of the few. Go goes so far to omit classic inheritance (sub-classing) at all: There is no way to implement an interface or extend an existing class like in Java or C#. That’s a bold design decision, but I really like it. In most projects I’ve came across, class inheritance caused a bunch of problems and there seems really no good reason to make use of it, except maybe for domain models. For the rare exception where you might need it, Go comes with the concept of embedding, the automatic delegation to subtype methods, which is a pretty cool and unique feature though.

In Go you can summarize the type concept like this: “If something can do this, then it can be used here” (from the official documentation). The following example (also from the official documentation) shows how to use the library function sort.Sort(sort.Interface) with any custom type. It only has to implement three methods (Len, Less and Swap):

type Sequence []int

// Methods required by sort.Interface.
func (s Sequence) Len() int {
    return len(s)
}
func (s Sequence) Less(i, j int) bool {
    return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

// Method for printing - sorts the elements before printing.
func (s Sequence) Print() string {
    sort.Sort(s)
    //...
}

(Note the elegant swap implementation!)

Scala also has limited support for Structural Types. It is possible to use them in generic type parameters. Here for example in the generic method makeNose():

object StructureTypeTest extends App {

  class Duck {
    def quack() = { println("Quack") }
  }

  class Frog {
    def quack() = { println("Quack") }
  }

  def makeNoise[T <: { def quack(): Unit }](quackable: T) = {
    quackable.quack
  }

  val duck = new Duck
  val frog = new Frog

  makeNoise(duck)
  makeNoise(frog)
}

The difference to duck typing is, if you’d pass an object to makeNoise() which doesn’t implement quack(), Scala would complain at compile time (and an IDE would immediately mark the code as erroneous)!

Below an interesting real world example: To simulate the automatic resource management in Java 7, we define a method using(), that takes any resource with a close() method, which is called after executing the given closure.

trait ResourceManagement {
  def using[A <: { def close(): Unit }, B](resource: A)(f: A => B): B =
    try {
      f(resource)
    } finally {
      try {
        resource.close()
      } catch { case _ => () }
    }
}

object file extends ResourceManagement {
  def copy(source: String, dest: String) = {
    using(new FileInputStream(source)) { int =>
      using(new FileOutputStream(dest)) { out =>
        out.getChannel()
          .transferFrom(int.getChannel(), 0, Long.MaxValue)
      }
    }
  }
}

//Usage
file copy ("test1.txt", "test2.txt")

So you can avoid cluttering the code with try/catch causes.

Leave a Reply

Your email address will not be published. Required fields are marked *


*