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
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
.
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, Post
s and Address
es aren’t really related. Forcing them to implement a common interface, e.g., Taggable
, compromises their definition.
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.
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.