Structural Typing: Compile Time Duck Typing

Posted on by in Everything Else

Google’s Go programming language, a statically typed compiled language, has been called a modern, better C. It builds on C by adding features such as garbage collection, concurrency constructs, and user-defined class-like types. One missing feature is classical object-oriented inheritance. Instead, Go uses interfaces and structural typing. Structural typing is like compile time duck typing. It makes Go feel like a dynamic language, such
as Ruby or Python.

Structural typing isn’t a new concept. But by making it an integral part of the language, Go has popularized it. Let’s take a look at it in Go and a few other languages.

Go’s Interfaces

An interface in Go is a group of methods that describe some related functionality. It’s similar to an interface in Java or C#. However, in Go, you don’t declare that a type implements an interface. Instead, a type just implements the methods in the interface. This lack of ceremony makes interfaces feel very simple and informal.

In the following example, a user-defined type implements the fmt.Stringer interface. fmt.Stringer consists of a single method: String() string. This method is used to define a string representation of a type. The generic fmt.Println function uses our user-defined type’s String() string method to convert a value of our type to a string.

This code makes no reference to the fmt.Stringer interface. It just implements its sole method and can now be used anywhere a fmt.Stringer is expected (in this case, by fmt.Println). Admittedly, a corresponding version of this example in Java would look very similar. The only difference is that in Go, our struct doesn’t say that it implements an interface. A small difference, but Go makes it feel overkill.

In this next example, our type implements another interface: http.Handler. Implementing this interface allows any type to serve HTTP requests (obviously this doesn’t make sense for our User type, it’s just a demonstration of implementing multiple interfaces).

http.Handler defines a single method: ServeHTTP(writer ResponseWriter, request *Request). The writer parameter is used to write out the response body. fmt.Fprintf provides printf style string formatting and writes to its given io.writer. io.Writer is another interface that defines a single method: Write([]byte) (n int, err error). http.ResponseWriter implements io.Writer.

Structural Typing in Scala

Scala also has its own version of compile-time duck typing.

This example contains two user-defined classes: Post and Address. A generic formattedTags method formats the tags of any object that has a tags: List[String] method.

A more traditional approach would have created a Taggable interface with a tags: List[String] method, have both Post and Address implement it, and have formattedTags take a Taggable. However, Posts and Addresses aren’t really related. Forcing them to implement a common interface, e.g., Taggable, compromises their definition.

OCaml’s Immediate Objects

The functional language OCaml uses structure when type checking object function parameters.

This example declares two immediate objects: user and movie. Immediate objects are like object literals in JavaScript. Notice how the OCaml REPL responses include the structure of the object in their type. Each of the immediate objects have a #to_json method, in addition to some other type-specific state and methods. We define a generic serialize jsonable function that expects an object that understands #to_json. This function feels dynamic but it’s type-checked at compile time.

No More Type Hierarchies

Structural typing is an interesting alternative to classical inheritance in statically typed languages. It allows you to write generic algorithms without obscuring the definition of a type in a sea of interfaces. Perhaps more importantly, it helps statically typed languages capture the feel and productivity of dynamically typed languages.