Embedding in Go

I've recently started on a new project in Go. I've found myself considering the implications of many of the language features recently. By far, the feature that is most interesting feature is the embedding of structs within each other.

Types in go

Go supports user defined types in the form of a struct which can have zero or more fields.

type Discount struct {
    percent float32
    startTime uint64
    endTime uint64
}

This gives a type Discount which can be instantiated. Furthermore, you can add methods which receive the type.

func (d Discount) Calculate(originalPrice float32) float32{
    return originalPrice*d.percent
}

Presuming you have an instance of Discount called holidaySale you can invoke the method using holidaySale.Calculate(49.99). But methods are not limited to receiving types only by value.

func (d *Discount) IsValid(currentTime uint64) bool{
    return currentTime > d.startTime && currentTime < d.endTime
}

The invocation is identical despite the differing of the receiver type holidaySale.IsValid(100.0). Golang transparently converts the value to a pointer when the method is invoked. The main reason to have a method receive a pointer type is so the method can mutate the referenced value, but it also can avoid a potentially costly copy.

Embedding

Types can also have zero or more anonymous fields.

type PremiumDiscount struct{
    Discount //Embedded
    additional float32
}

Including an anonymous field in a struct is known as embedding. In this case the Discount type is embedded in the PremiumDiscount type. All the methods of Discount are instantly available on the PremiumDiscount type. Furthermore those same methods can be hidden

func (d *PremiumDiscount) Calculate(originalPrice float32) float32 {
    return d.Discount.Calculate(originalPrice) - d.additional
}

In this example the Calculate method receives a pointer to a PremiumDiscount. It calls the Calculate method that receives a pointer to a Discount and subtracts an additional amount off of it. This is possible because anonymous fields can still be referenced as part of the struct by using the full name of the embedded struct.

While this may all look very similiar to inheritance and method overriding in a language like Java, it is only superficially similar.

By-Value

The most logical way to embed a struct is by-value. This is similar, but not equivalent to the inheritance mechanism in an object oriented language.

type Parent struct{
    value int64
}

func (i *Parent) Value() int64{
    return i.value
}

type Child struct{
    Parent
    multiplier int64
}

func (i Child) Value() int64{
    return i.value * i.multiplier
}

In the context of the above example, a call to myParent.Value() returns the value field if myParent is an instance of Parent. Using a similarly named instance of Child, calling myChild.Value() returns the product of the value and multiplier fields. This is fundamentally different than method overriding because a call to myChild.Parent.Value() still invokes the method receiving a *Parent, not the one receiving Child. Both method calls are resolved at compile-time without using any sort of virtual table.

There is nothing explicitly bad about this. Consider the following interface in the context of this example

type Valueable interface {
    Value() int64
}

This interface requires a type to implement a Value method. The Parent type meets this interface. Since the Child type embeds the Parent type it also meets this interface. However, if an instance of Child is used from a context referring to it as a Valueable, there is no easy way to invoke the Value method receiving a *Parent. It is still possible by using type assertions or reflection.

There is an important distinction to observe here. If myParent is an instance of Parent the value myParent cannot act as a Valueable. You must use the the value &myParent, a pointer to the instance, to act as a Valueable. This is because the method Value receives a *Parent not a Parent.

Although the value myParent cannot satisfy the interface Valueable, the value myChild can! This is because the Value method receives a Child by value. The pointer &myChild satisfies the interface as well. You may not actually want to use myChild to satisfy the interface as it creates a copy. This means that any further changes to myChild would not be seen by anything using the interface.

Using Child and Parent to satisfy the Valueable interface is still not traditional inheritance and method overriding. You could argue that this is example is equivalent to this C++ example

class Valueable{
    public:
        virtual int Value() = 0;
};

You could go on to define a class named Parent and another one named Child. But Parent would need to explicitly inherit from Valueable, which is not the case with interfaces in Golang. The most prominent difference is that there is no way that usage of a Valueable* abstract type could result in the copy-on-assignment semantics that are possible with interfaces in Go.

Exploring this further, the Child class could not implement any other abstract classes in C++ without inheriting from them. At first this seems like a difference of syntactic sugar. In C++ you are required to explicitly indicate what abstract classes you inherit from. In Go, you either meet the interface or you do not. The compiler has to check that you actually do this in either language, so Go gains some brevity by using this. But also, you have the possibility of defining an interface to code you don't maintain or cannot otherwise change. As an author, I could write many different types which all implement the Value method. At any point in time, someone else could create the Valueable interface and use instances of my types to satisfy their interface without ever requiring me to change my code. This gives you the ability to use a type from one framework in another framework without needing a wrapper type and a large number of wrapper functions. In C++ this difference is irreconcilable because of the fact that one framework defines and implements an Foo::Reader and the other one implements a Bar::Readable. The two types could have exactly the same read function, but you'd still need a bunch of glue to make one library work with another.

The real problem with comparing Go interfaces to abstract classes in C++ is that eventually you wind up having to deal with diamond inheritance at some point. We're all capable of dissecting the hundreds of lines of compiler errors that can result from some situation of ambigious parent classes, but at the end of the day all that effort doesn't actually get you any closer to solving the problem at hand. Of course Java "solves" this problem by declaring thou shalt not have multiple inheritance. But that doesn't really slow anyone down as they add sixteen different interface specifications to a single type.

You know not what you do(literally)

There is still a fundamental issue with by-value embedding in Go. If the type you embed has non-exported fields and you are in a separate package, those fields are completely inaccessible to you. Sure, you can embed a HTMLElementTree, but if you cannot initialize the internals of it you have no idea what it is good for. Furthermore, the type embedding HTMLElementTree gains a bunch of methods which can be invoked which are either useless or dereference nil when called.

A common idiom in Go is to define a NewX method which returns a pointer to a ready to use instance. For example the included json package in Go defines NewDecoder for decoding JSON from streams. But since NewDecoder returns *Decoder you are stuck with a pointer. You could dereference it and create a copy during the initialization of something that embeds it, but that would result in an object being created on the heap with a very short lifecycle. There is nothing prohibiting the creation of Init methods that receives a pointer to a type and does all the work needed to set it up.

All of this leads to a very strong arugment that exported types in Go should be useful by default. RAII is a sound concept, but the complexity that it ultimately leads to in C++ is not enviable.

All this consideration has assumed that you control the instantiation of what is embedded. Proceed on to the next section.

By-Pointer

You can embed by-pointer in go

type Bitmap struct{
    data [4][4]bool
}

type Renderer struct{
    *Bitmap //Embed by pointer
    on uint8
    off uint8
}

func (r *Renderer)render() {
    for i := range(r.data){
        for j := range(r.data[i]){
            if r.data[i][j] {
                fmt.Printf("%c",r.on)
            } else {
                fmt.Printf("%c",r.off)
            }
        }
        fmt.Printf("\n")
    }
}

The Renderer type embeds a Bitmap by-pointer. The first advantage to this is that you can rely on functions that use the NewX idiom returning a struct by-pointer to do initialization. The second advantage is that you can embed all the functionality of a type without needing to know when it is instantiated. The embedded pointer to a Bitmap is no different than any other pointer in Go, so it can be assigned multiple times. By doing this you can change what instance you are extending dynamically at run time.

In this example, a single instance of Bitmap can act as the embedded instance of many Renderer instances

var renderA,renderB Renderer
renderA.on = 'X'
renderA.off = 'O'
renderB.on = '@'
renderB.off = '.'

var pic Bitmap
pic.data[0][1] = true
pic.data[0][2] = true
pic.data[1][1] = true
pic.data[2][1] = true
pic.data[3][1] = true

renderA.Bitmap = &pic
renderB.Bitmap = &pic

renderA.render()
renderB.render()

This shares the same Bitmap instance to two different renderers. Each renderer has its own set of characters, allowing two representations of the bitmap to be printed. This is what the output looks like.

OXXO
OXOO
OXOO
OXOO
.@@.
.@..
.@..
.@..

This example demonstrates the Flyweight Pattern. Although inconsequential to memory consumption in this example, having many thousands of instances sharing a single underlying data structure can be very significant in reducing the memory consumption of systems.

Applying this to Interfaces

User defined types in Go are not restricted to embedding just a struct. They can actually embed an interface type as well.

One consequence of this is that anything embedding an interface satisfies that interface. You can use this to create objects which add behavior to an existing interface.

package main

import "io"
import "os"
import "fmt"

type echoer struct{
    io.Reader //Embed an interface
}

func (e * echoer) Read(p []byte) (int, error) {
    amount, err := e.Reader.Read(p)
    if err == nil {
        fmt.Printf("Overridden read %d bytes:%s\n",amount,p[:amount])
    }
    return amount,err
}

func readUpToN(r io.Reader, n int) {
    buf := make([]byte,n)
    amount, err := r.Read(buf[:])
    if err == nil {
        fmt.Printf("Read %d bytes:%s\n",amount,buf[:amount])
    }
}

func main(){
    readUpToN(os.Stdin,3)

    var replacement echoer
    replacement.Reader = os.Stdin

    readUpToN(&replacement,3)
}

In this example, the embedded interface is io.Reader. The os.Stdin object is the standard input stream and satisfies that interface. Calling readUpToN the first time reads and displays 3 bytes. The second call to readUpToN uses an instance of echoer that embeds os.Stdin. Since there is a receiver method for the *echoer type it is called. In the example it also displays the text. The result looks like this.

ericu@luit:~$ echo 'ABCDEF' | ~/go/bin/embedinterface 
Read 3 bytes:ABC
Overridden read 3 bytes:DEF
Read 3 bytes:DEF

There is no such thing as embedding an interface by-pointer. Trying to do something such as

type echoer struct{
    *io.Reader //Embedding an interface by pointer
}

generates the following error

../go/src/hydrogen18.com/embedinterface/main.go:8: embedded type cannot be a pointer to interface

Conclusion

Go offers the embedding mechanism as an alternative to the inheritance mechanism in traditional object oriented programming languages. I feel it offers the correct level of flexibility without levying additional mountains of complexity on the language. I will not really know how I feel about Go until I complete this project, but this has been a terrific educational experience.


Copyright Eric Urban 2013, or the respective entity where indicated