Go's interface{}
- equivalent, for our purposes, to (void *)
in C - allows us enough leniency in the type system to implement a perversion of haskell's perversion of category theory's concepts of functors and nomads (and more, but I'm not going there until I have some limited form of generics, and therefore proper type-checking). All code mentioned here is also on github.
Starting with functors. In category theory, a functor is a one-to-one mapping between two "categories" (classes of objects and functions on, or relationships between, those objects) such that all relationships within the category are preserved. For example, in the category of integers, with the functions of addition and subtraction, we might create a functor mapping to the category of powers of two, with the new operations being multiplication and division by two. Being one-to-one, this functor is two-way.
In haskell, this is limited to the idea of lifting "real" things into new categories; the new category usually act as a sort of box. There is no symmetry between the two worlds, nor is there any reverse transformation; however, any manipulation that can be performed in the "real" world can be performed as easily in the new one. A good example of this idea, which exists in most modern languages, is the map
function, which applies a given operation to every element in a list. A list can be thought of as a haskell-style functor: any real-world transformation can be lifted and, when the result is applied to the list, the underlying transformation is applied to each individual element in the list. The real-world transformation is lifted by the fmap
function.
Another useful example of a functor is the maybe
construct. A single maybe
instances is either nothing, or some underlying value. This allows us to represent the idea that a computation might have "failed" (found no solutions, say) without introducing unwanted error checking. An operation applied to a maybe
construct should either apply to the underlying value in the normal way, or preserve "nothing" if there is no underlying value.
In go, an implementation of a list as a functor might be
type List struct { data []interface{} } func NewList() *List { return &List{ []interface{}{} } } func (l *List) Append(v interface{}) { l.data = append(l.data, v) } func (l *List) Fmap(f func(interface{}) interface{}) { for i, v := range l.data { l.data[i] = f(v) } }
Since we have no generics, this isn't type-safe, and the function passed to Fmap
must be awkwardly typed func(interface{})interface{}
. Implementation of the maybe
functor is left for the reader.
Since so much time has been wasted debating how or whether to implement generics in go (I say wasted only because there has been, after 2 years of shot down proposals, still no solution or even a viable, widely-accepted candidate), it would be an unpardonable break from tradition for me to pass up this opportunity to do so now.
Tough.