Tuesday, November 17, 2009

A Mental Model of Types and Methods in Go

I've been spending the past few days since Go's release wrapping my head around it. The language site has a lot of good information, but I wanted to write up my understanding of one part of the type system.

First off, while they're types in terms of syntax, I see Go's interfaces as semantically distinct; for most of this post, when I say "type" I mean a concrete or non-interface type.

Aside from those, Go has a lot of built-in types. To build a user-defined type, one simply aliases that type to a built-in type or to some other user-defined type. The built-in type defines the format of an instance in memory, but defines no methods. Methods are attached to user-defined types; which methods a type has may depend on various things I won't get into right now, but this set is fixed for any particular concrete type.

Interfaces act as a layer on top of user-defined types. A variable with an interface type can contain any instance of any concrete type whose method set includes all of the methods in the interface. Go is rather unusual in that no explicit declaration is necessary for a concrete type to implement an interface -- if the methods are there, the interface is considered to be implemented. Among other things, this means that an interface can be created after the fact to describe functionality common to several types, and all of those types will automatically be compatible with variables declared as instances of that interface.

This means that any particular variable carries three pieces of information. First, there's the actual value; this is not directly accessible via interface variables but present in all cases. Second, there is the runtime type -- that is, the concrete type of the variable; this is what reflect.Typeof will tell you about, and defines the method set that will be used. Finally, there is the compile-time type, which is either the runtime type or some interface it implements; this determines which methods can actually be called and whether access to the value is possible.

Each of these bits of information can be changed relatively independently. A type assertion can change the compile-time type without affecting either the runtime type or the value. A (non-numeric) conversion can change the runtime type as well as the compile-time type, as long as the in-memory representations are identical. And of course changes to the value will in general not affect either type associated with an instance.

One interesting result of this is that you can use conversions to swap out the method set of an instance in runtime. For example, sort.StringArray defines methods on a []string that define a particular sort ordering. One could easily write a ReversedStringArray type that induces a backwards sort. Applying conversions, it would even be possible to take the same array and sort it in different ways at different points in the program, all using the sort package, just by telling Go to treat the array as a different runtime type each time.


  1. "First, there's the actual value; this is not directly accessible via interface variables but present in all cases."

    See type assertions: http://golang.org/doc/go_spec.html#Type_assertions

  2. @Bob: You mean the ones I link later in the post? They do allow access to the value, but this use is syntactically indirect, and imposes a (generally negligible but nonzero) runtime cost for the type check.


  3. I have not heard about before. Keep sharing more blog posts on this Community and get extra benefits with this ideas and knowledge. Thanks for this one.
    technocompe giikers
    techstarusa allmusic

  4. thanks for sharing best topics, keep such update and share best ideas over to all and me so i very happy with this.
    good tips
    nice tips provide

  5. This blog is so nice to me. I will continue to come here again and again. Visit my link as well. Good luck
    obat aborsi
    cara menggugurkan kandungan