prose :: and :: conz


Static typing doesn’t have to suck: Structural Typing

The previous post in my “Static typing doesn’t have to suck” series (now that I’m on post number three, I think it’s time to declare it a “series”) introduced implicit conversions. Today, we’re going to take a look at different classes of typing. Classes of typing are distinguished by how they define type equality. Let’s first talk about Java’s typing which is nominative typing. Loosely-speaking, this means that two types are equal if and only if they have the same fully-qualified name. I’ll start with the ubiquitous duck example to illustrate nominative typing. Let’s define a Java class for our duck:

public class Duck {
    public void quack() {
        System.out.println("QUACK!!!");
    }
}

Using nominative typing, we believe the type of another object is a Duck if the type of that object is named “Duck”. For instance, we can only pass an instance of an object to a method which requires a Duck if that object was instantiated as a type with the exact name Duck. However, this is not the only way we could consider two types equal. We could say “if it quacks like a duck, then it’s a duck”. That is to say, if it has a method quack() which takes no arguments and returns no values, then it is the same type. As a result, this class would also be a duck:

public class Cat {
    public void quack() {
        System.out.println("Meow!!!");
    }
}

In dynamically typed languages, this is called “duck typing.” In your dynamic code, you’ve had no type labels slung asunder. When runtime rolls around and quack() is called, it will resolve to that object’s quack() implementation. There have been times I’ve wished I could do this in my Java code. I wanted to just call some method like save() and I didn’t care what the type was. I just needed something that could be saved. If I was fortunate, save() was declared in an interface that I could sprinkle on the classes I want to work with. However, more often than not, this save() method was declared along with a slew of other methods, so I couldn’t simply implement the existing interface. Or worse, it could be declared as part of a class in 3rd-party code that I can’t do anything to change. Even if I was able to do the refactoring, it’s annoying that I have to create this new type and sprinkle it around. I just want to call save() on stuff with a save() method! Stupid static typing. I guess I’ll have to use reflection…

Like many of my former distastes with static typing, it was a problem of the particular type system in Java, not static typing in general. It is certainly possible to have static type checking with a close relative of duck typing: structural typing. I believe it’s fair to say that structural typing is duck typing that is checked at compile time. Scala, as you probably can guess, has this feature. Let’s first look at a nominative Duck implementation:

class Duck {
  def quack = Console.println("QUACK!!!")
}

If we want a class that can call quack, we need to refer to the name of the type Duck:

object Caller {
  def callQuack(d:Duck){
    d quack
  }
}

Perhaps it’s inconvenient or undesirable to provide or expect a Duck class. We just want something that quacks. Then we use structural typing, and it looks like this:

object Caller {
  def callQuack(d:{ def quack }){
  d quack
  }
}

The argument type is declared here as an anonymous class. There is a bit of an unfortunate catch, though. Our type that quacks is pretty simple. VERY simple for that matter. If there are more methods in our type, it could get ugly. That’s not really a problem, unless you want to use it more than once. If we have multiple functions that use this type, we violate DRY. We’ll stick with our duck example, but look at how we might have to declare that structure multiple times:

object Caller {
  def callQuack(d:{ def quack }){
    d quack
  }

  def printThenCallQuack(d:{ def quack }) {
    Console.println("Hey, look! A duck!")
    d quack
  }
}

Well that sucks. Thankfully Scala has an answer for this. You can use their handy type alias to give the structure a name. So we’ll declare a type named “Duck” for this structure:

object Caller {
  type Duck = { def quack }

  def callQuack(d:Duck){
    d quack
  }

  def printThenCallQuack(d:Duck) {
    Console.println("Hey, look! A duck!")
    d quack
  }
}

So… now we’ve come full circle back to nominative typing. Well, not quite. There is a subtle difference here. Type aliases are more like a code macro. Imagine that the compiler finds everywhere I’ve used Duck in the scope of my Caller object, and swaps it out for the definition of the type. The name “Duck” is erased at compile time. This is opposed to the previous nominative implementation which would have produced byte code named Duck.class. As you can see below, we can call these two functions with stuff that isn’t named duck at all:

class Cat {
  def quack = Console.println("Meow!!!")
}

object Structural extends App {
  Caller.callQuack(new Cat)
  Caller.printThenCallQuack(new { def quack = Console.println("Anonymous!") })
}

It’s even better than having your cake and eating it too. You get all three: static typing, duck/structural typing, and DRY.

It’s worth noting that nominative typing is a subset of structural typing. If the name is the same, then certainly the structure will be the same. So for most of my code, I’ll continue to prefer the plain nominative approach. However, I’m going to keep this trick in mind whenever I develop a library. It would certainly reduce the consumer’s coupling to my library if I don’t require him to extend a trait to interact with me.

If you decide to use structural typing in Scala, there is one caveat. Despite the successful design effort to decouple the Java language from the JVM runtime, the JVM is fundamentally nominatively typed. Since there is not a name label associated with a structural type, it must be resolved at runtime with reflection. Most of the time the overhead won’t be noticeable, but bear it in mind.

While all of this is great, I feel a little bit like we’re still nominative. No, not because we used a type alias. It’s the fact that we have a method/function named “quack”. That still feels not quite structural to me. What if we just wanted to know that it’s a function that took nothing and returned nothing regardless of name? If that piques your interest, consider your appetite whet for my future post on lambdas. And I hope you’re proud Mom… I worked in some of my country upbringing with the duck… Yes, you may assume that it’s a Mallard.


Olde Comments
  1. […] like implicit conversions and still have the assurance of static typing. Keep your eyes open for a future post on structural typing, which is a completely foreign concept to Java. Soon after that I plan to get to lambdas, which […]

  2. […] post will conclude my little series on why I’ve joined the static typing camp. Lambdas are pretty common in dynamically-typed […]

Tagged with: scala (41), java (22), static-typing (16)