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.
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 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.
printf style string formatting and writes to its given
io.Writer is another interface that defines a single method:
Write(byte) (n int, err error).
Structural Typing in Scala
Scala also has its own version of compile-time duck typing.
This example contains two user-defined classes:
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
Address implement it, and have
formattedTags take a
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:
#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.