Learning to Go, Part I: Interfaces

Published:

This article was originally published in the April 2014 issue of NFJS the Magazine.

This article begins an introductory series on the Go programming language. Go is a language optimized for large-scale software engineering and is rapidly becoming the language of choice for building cloud services. It does this in a very interesting way, optimizing for simplicity rather than complexity and taking a “less is exponentially more” approach.

Three ways in which this really stands out are:

  • A pragmatic approach to component design (through implicitly satisfied interfaces)
  • The need to forget much of what you’ve learned about object-oriented programming (and to prefer composition over inheritance)
  • A concurrency model that mere mortals can use to great effect (via goroutines and channels)

Overall, it’s a language with the productivity of a dynamically-typed, interpreted language with the safety and efficiency found in a statically-typed, compiled language.

This series will focus on learning Go from the perspective of Java developers. We’ll begin with design ideas informed by Go’s primary method of abstraction: interfaces.

What If I Told You Classes Were Unnecessary?

Object-oriented programming (OO) has dominated large-scale software engineering for quite some time now. While OO finds its roots in simulation programming, it moved into and began to dominate the mainstream of business programming with the advent of languages like C++ and Java. When we design programs in an OO language, we usually begin with an object model, defining a taxonomy of objects representing all of the entities, or nouns, in our business domain. Languages like C++ and Java support this via the class construct, with a class defining a template for creating a particular type of object. Classes normally can be extended, creating subclasses. This allows us to define a hierarchy of types that inherit characteristics and behavior from one another.

But what if I told you that classes were unnecessary? This may seem like heresy, but we already know it. JavaScript has the feel of an OO language, but it does not have classes at all. In fact, it utilizes what’s called prototype-based programming 1. With prototype-based programming, we achieve reuse by cloning existing objects that serve as prototypes.

Those of us that have surfed the wave of functional programming have discovered that it’s indeed possible to structure large programs around a very basic set of types (sets, lists, maps, etc.), along with a large collection of behaviors, or functions, that can operate on those types. In these languages, we don’t really see anything resembling an object!

The Go programming language is somewhat unique in that it offers many OO-like constructs, but does not offer either classes or prototypes. But it is also not correct to refer to it as a functional language per se.

Favoring Composition Over Inheritance

While the Java programming language has always included a type system featuring inheritance, it has long been considered best practice to favor composition over inheritance. When Joshua Bloch, now Chief Java Architect at Google and then Distinguished Engineer at Sun Microsystems, wrote his seminal work Effective Java 2, he included as Item #16: “Favor composition over inheritance.”

When we use inheritance, we must be careful to implement subclasses such that they are in an is-a relationship with their parents"In other words, any context that expects an instance of the parent type should also work well with an instance of the subtype. We call this substitutabilty, which is expressed well by the Liskov Substitution Principle 3. When we don’t follow this principle, we usually end up creating a lot of code that uses type checking to implement special behaviors. In doing so, we create fragile systems that don’t abide by the Open-Closed Principle 4.

When we instead utilize composition, we build classes that are in a has-a relationship with their components. If we’d like to encapsulate the relationship between a class and its components, we can apply delegation, forwarding method calls to the underlying component. This makes it appear as if our object is-a instance of another type in terms of behavior, without the problems associated with inheritance-based reuse.

The Go programming language also emphasizes composition over inheritance, in that it does not even provide inheritance as a language construct! Instead, it provides the ability to use composition at the type and behavior level. Let’s see how.

Structs FTW!

In Go we can define composite data types using structs. You may be familiar with structs if you’ve spent any time programming in C. Structs are simply a sequence of fields, each of which has a name and a type. If you take a look at A Point Struct, you’ll see that we’ve defined a struct representing a point in Cartesian space.

type Point struct {
	X, Y float64
}

A Point struct

New Go types are defined using the type keyword. We give our type the name Point. It’s important to note that we’ve used uppercase here. Visibility in Go is determined by the case of an identifier. All code in Go is defined within a package. If you’d like your types and functions to be visible beyond the confines of your package, you need to export them by starting them with an uppercase letter. If you use a lowercase letter or underscore (_), they will be unexported, roughly equivalent to the Java concept of private.

You’ll quickly notice that we’ve defined two fields in our struct, X and Y, both of which take on the type float64, representing a 64-bit floating-point number.

NOTE: Unlike Java, Go places the type declarations after the field names (the same applies when declaring function arguments) - you’ll get used to this eventually!

The keen observer will also note the case of the field names. Just as case matters when dealing with type visibility beyond package boundaries, case also matters when dealing with field visibility beyond struct boundaries. Because both X and Y are uppercase, they are exported from the struct. Had they been x and y, they would be unexported, and therefore only visible within the struct. To find out how we’ll deal with that, hold on until the next section!

Update (2014-07-09): My understanding of this point was corrected on Reddit. Case only affects visibility with respect to packages, so x and y would also be accessible to any code within the package enclosing the struct. See this example.

So how do we create one of our Point’s? We have a couple of options. First, we can utilize the new function:

p := *new(point.Point)
p.X = 1
p.Y = 2

We prefixed our call to new with an asterisk (*) in order to dereference the pointer returned by new. Yes, that’s right, Go has pointers. With that said, there’s no pointer arithmetic, so they’re not quite as scary as what we find in languages like C and C++. They almost represent a happy medium between C and Java.

NOTE: You also don’t have to worry about freeing memory! Go is a garbage collected language.

Built-in types in Go start zeroed, rather than nil. In the case of float64, they take on the value 0. It’s good practice for our types to start zeroed as well, and we get that for free here. Our newly initialized Point represents the Cartesian coordinate (0,0). The next two statements move our Point to (1,2).

We can eliminate the two step process by utilizing a composite literal:

p = point.Point{X: 1, Y: 2}

A composite literal creates a new instance each time it is evaluated, and initializes each field with the given value. In addition, this particular Point starts out dereferenced. If we actually want a pointer to it, we need to prefix the literal with an ampersand (&).

NOTE: It is generally considered idiomatic Go to prefer the composite literal style to using new.

Enough about structs, on to methods!

Methods

Go methods are just functions that happen to have a receiver argument. You can find one in Method for translating Point’s.

func (p Point) Translate(xDist float64, yDist float64) Point {
	return Point{p.X + xDist, p.Y + yDist}
}

Method for translating Point’s

The Translate method takes two distances along the x and y-axes, and translates a point from its current position to a new position by returning a new Point literal that combines the current x and y coordinates with those distances. We call Translate in Calling Translate, and the output is found in Output of calling Translate.

q := p.Translate(5, 5)
fmt.Printf("Translated %v to %v\n", p, q)

Calling Translate

$ go run point.go
Translated {1 2} to {6 7}

Output of calling Translate

Because we’re not operating on a pointer to a Point, there’s no way for us to affect the existing instance. If we couple this with unexported fields, we’d have completely controlled access to a Point instance’s fields.

If we want to mutate a Point, we need our method to use a pointer as the receiver argument rather than a value. We do this in Method for translating a Point via a pointer.

func (p *Point) TranslatePointer(xDist float64, yDist float64) {
	p.X = p.X + xDist
	p.Y = p.Y + yDist
}

Method for translating a Point via a pointer

As you can see, rather than creating a new Point and returning it, this method returns no value and directly mutates the Point referred to by the pointer passed in as a receiver. Notice that Go conveniently dereferences the pointer automatically when using the dot (.) operator to access fields. We call this method on a pointer to a Point in Calling TranslatePointer (with output in Output of calling TranslatePointer), again using an ampersand (&) to tell Go we’d like a pointer to a Point, not a Point value.

qP := &point.Point{X: 1, Y: 2}
qP.TranslatePointer(5, 5)
fmt.Printf("Translated using pointer to %v\n", qP)

Calling TranslatePointer

$ go run point.go
Translated using pointer to &{6 7}

Output of calling TranslatePointer

Now that we have the capability to create types with associated methods, let’s look at how we can compose new composite types from existing ones.

Type composition with structs

Let’s imagine that we’d like to extend the concept of Point and add the notion of color. If we were working in Java, we might do the following:

public class ColorPoint extends Point {
  private Color color;

  // rest omitted...
}

Since Go doesn’t have the concept of classes or inheritance, we need to work with structs to accomplish our goal. We can compose a type called ColorPoint by embedding the Point type and then adding the additional field representing color (Embedding Point into ColorPoint).

const (
	BLUE  = iota
	RED   = iota
	GREEN = iota
)

type ColorPoint struct {
	Point Point
	Color int
}

Embedding Point into ColorPoint

First note the use of the const keyword. We’re defining a set of integer constants to represent our colors. This is the closest approximation that Go has to an enumerated type (such as Java’s enum) and is usually more that sufficient. The predeclared identifier iota represents successive untyped integer constants. It is reset to 0 whenever const appears again in the source. This has the effect in our code of setting BLUE to 0, RED to 1, and GREEN to 2.

Next we define our ColorPoint type as a struct. We embed Point as an exported field also called Point, and we define an additional exported field called Color that is typed as an int. Now, in Creating and printing a ColorPoint, we’ll create an instance of ColorPoint, and then we’ll print out both the previously defined Point instance as well as the ColorPoint instance (results in Output of creating and printing a ColorPoint).

r := point.ColorPoint{Point: point.Point{X: 1, Y: 4}, Color: point.BLUE}

fmt.Printf("Point: %v\n", p)
fmt.Printf("Color Point: %v\n", r)

Creating and printing a ColorPoint

$ go run point.go
Point: {1 2}
Color Point: {{1 4} 0}

Output of creating and printing a ColorPoint

We’ve now successfully created a composite type, but we’re missing something. Let’s press on.

Houston, we have a problem…

Here’s a summary of our problem:

  • We have Point’s.
  • We have ColorPoint’s.
  • ColorPoint’s are like Point’s, but they are definitely not Point’s since Go does not have inheritance.
  • That said, we’d like to be able to write methods that can interoperate between them. What do we do?

As an example, since both Point and ColorPoint have x and y coordinates, it might be interesting if we could compute the Euclidean distance 5 between them. As a reminder, the formula can be found in Euclidean distance formula.

Euclidean distance formula

Euclidean distance formula

As expressed here, you can think of p1 and q1 as the x coordinates for the two points, and p2 and q2 as the y coordinates for the two points. So how do we implement a method that will allow us to compute this formula in such a way that will work with Point’s, ColorPoint’s, or both?

Go Interfaces

Fortunately Go contributes a very powerful version of interfaces to the abstraction conversation. Go interfaces describe groups of behaviors in the form of method signatures that implementors of the interface must implement. So far this doesn’t sound unlike Java interfaces. Let’s take a look at two Go interfaces in Positioner and Distancer interfaces.

type Positioner interface {
	Coordinates() Point
}

type Distancer interface {
	DistanceTo(p Positioner) float64
}

Positioner and Distancer interfaces

Notice that interface’s are themselves types. This is important, because it allows us to define function and method signatures that accept interfaces as arguments. Positioner’s Coordinates() method should provide us the position of any implementor in terms of a Point. Distancer’s DistanceTo() method will calculate the distance between any implementor and a Positioner.

NOTE: It is idiomatic in Go for interface names to be suffixed with “-er.”

However, unlike Java interfaces, where we might write:

public class Point implements Positioner, Distancer {
  // implementation omitted...
}

Go does not have an implements keyword. In fact, if you think about the way we’ve defined structs and methods so far, the only place in which we indicated any attachment between a struct and a method is in the method signature itself, in the form of the receiver parameter. So how does this work in Go?

Let’s talk about how interfaces are satsified.

In languages like Java, an interfaces is satisfied explicitly. Classes are tagged with implements Interface. The compiler then looks up that interface and identifies all of the method signatures defined by it. It then examines the class to ensure that all of those method signatures have a concrete implementation in either the class itself or one of its parents.

In Go, interfaces are satisfied implicitly. We do not tag structs in any way. For a type to implement an interface, it simply needs to implement all of the method signatures defined by that interface. When we use a type in the context of an interface (e.g. we pass a type into a function that expects an interface as one of its arguments), the compiler will check to see if that type satisfies the interface.

Let’s see how this falls out in practice.

First, let’s define a function that can calculate the distance between two Positioner’s (Calculates the distance between two Positioner’s).

func distanceBetween(a Positioner, b Positioner) float64 {
	p := a.Coordinates()
	q := b.Coordinates()
	sqOfXDist := math.Pow(p.X-q.X, 2)
	sqOfYDist := math.Pow(p.Y-q.Y, 2)
	return math.Sqrt(sqOfXDist + sqOfYDist)
}

Calculates the distance between two Positioner’s

Next, in Point satisfies Positioner and Distancer, we satisfy both interfaces for Point.

func (p Point) Coordinates() Point {
	return p
}

func (p Point) DistanceTo(pos Positioner) float64 {
	return distanceBetween(p, pos)
}

Point satisfies Positioner and Distancer

In ColorPoint satisfies Positioner and Distancer, we satisfy both interfaces for ColorPoint.

func (cp ColorPoint) Coordinates() Point {
	return cp.Point
}

func (cp ColorPoint) DistanceTo(pos Positioner) float64 {
	return distanceBetween(cp, pos)
}

ColorPoint satisfies Positioner and Distancer

This all results in our ability to interchange Point’s and ColorPoint’s in calls to DistanceTo() (Calculating the distance between Point and ColorPoint). The output of these calls is found in Output of calculating the distance between Point and ColorPoint.

fmt.Printf("Dist b/w p and q = %v\n", p.DistanceTo(r))
fmt.Printf("Dist b/w q and p = %v\n", r.DistanceTo(p))

Calculating the distance between Point and ColorPoint

$ go run point.go
Dist b/w p and q = 2
Dist b/w q and p = 2

Output of calculating the distance between Point and ColorPoint

At first glance this may not seem so impressive, nor may it seem to be that great of an advantage over what’s available to us in Java. The power, however, is hiding just under the surface. When we think about implementing an interface in Java, we usually are thinking in terms of taxonomy. Classes extend other classes, and as we’ve previously stated, that means that a child class ought to be substitutable for its parent 3. When we implement an interface in Java, we are also usually thinking that the class is a version of that interface. We might have expressed our Cartesian coordinate taxonomy in Java as:

public interface Coordinate {
  double distanceTo(Coordinate c);
}

public class Point implements Coordinate {
  // implementation omitted
}

public class ColorPoint extends Point implements Coordinate {
  // implementation omitted
}

When we do this, we soon hear ourselves talking about Coordinate’s as things, not as groups of behaviors. Let’s contrast this with Go’s implicit satisfaction by adding an additional example. Perhaps our program’s purpose is to keep track of animals in a wildlife preserve. It’s quite natural that we’d have an Animal type, and that type would have some way of keeping track of the animal’s current location (Animal struct).

type Animal struct {
	Name string
	X, Y float64
}

Animal struct

In order to perform our desired distance calculations, we need Animal to satisfy our two interfaces (Animal satisfies Positioner and Distancer).

func (a Animal) Coordinates() point.Point {
	return point.Point{X: a.X, Y: a.Y}
}

func (a Animal) DistanceTo(pos point.Positioner) float64 {
	thing := pos.Coordinates()
	sqOfXDist := math.Pow(a.X-thing.X, 2)
	sqOfYDist := math.Pow(a.Y-thing.Y, 2)
	return math.Sqrt(sqOfXDist + sqOfYDist)
}

Animal satisfies Positioner and Distancer

NOTE: Because distanceBetween() was not exported from the point package, we cannot use it in the animal package. Sometimes you’ll run into this situation in Go, which prefers “dependency hygiene” over reuse.

And now, we can perform our desired calculations (Mixing Animal’s and Point’s using interfaces). We now know the distance between our penguin and our original point p, and we also know that given the proximity of the seal (Output of mixing Animal’s and Point’s using interfaces), our penguin needs to start running!

penguin := animal.Animal{Name: "penguin", X: 1, Y: 1}
seal := animal.Animal{Name: "seal", X: 1, Y: 4}

fmt.Printf("Dist b/w penguin and seal = %v\n", penguin.DistanceTo(seal))
fmt.Printf("Dist b/w penguin and point = %v\n", penguin.DistanceTo(p))

Mixing Animal’s and Point’s using interfaces

$ go run point.go
Dist b/w penguin and seal = 3
Dist b/w penguin and point = 1

Output of mixing Animal’s and Point’s using interfaces

Now for the test. Is it proper to think of an Animal as being a Distancer or Positioner in terms of taxonomy? Not really. In fact, that seems like a coupling of concerns. And if we were implementing this program in Java, a naive translation would probably cause us to do the following:

public class Animal implements Positioner, Distancer

So to summarize, Go interfaces allow us to use arbitrary types in contexts expecting a particular interface type, as long as the type in question implements all of the methods defined by that interface. Since interfaces are satisfied implicitly, we’re no longer pressured toward treating interfaces as part of a type taxonomy. Instead, we’re able to focus on them as groups of related behaviors.

This may seem like a concept popularized by dynamic languages called “duck typing” 6. In most cases, you call a method blindly, and allow the runtime dispatch system to determine if the object can respond to that method. While this is a similar concept, it is not a very good description of how Go works. Go actually employs “structural typing” 7, which uses the compiler to determine interface satisfaction in a type safe manner.

Conclusion

I hope you’ve enjoyed this brief introduction to the Go programming language, as well as one of its most powerful features: interfaces. With interfaces we’re able to take a more pragmatic approach to component design, as we’re not forced to think in terms of taxonomies and deep type hierarchies. In the next installment we’ll explore Go’s approach to concurrency. Until next time my fellow gophers!


  1. Prototype-based programing, Wikipedia. http://en.wikipedia.org/wiki/Prototype-based_programming ↩︎

  2. Bloch, Joshua. Effective Java: Programming Language Guide. Addison-Wesley, 2001. ↩︎

  3. Liskov substitution principle, Wikipedia. http://en.wikipedia.org/wiki/Liskov_substitution_principle ↩︎ ↩︎

  4. Open-closed principle, Wikipedia. https://en.wikipedia.org/wiki/Open-closed_principle ↩︎

  5. Euclidean distance, Wikipedia. http://en.wikipedia.org/wiki/Euclidean_distance↩︎

  6. Duck typing, Wikipedia. http://en.wikipedia.org/wiki/Duck_typing ↩︎

  7. Structural type system, Wikipedia. http://en.wikipedia.org/wiki/Structural_type_system ↩︎