Go is unique in many ways. Part of its value is in combination of traditionally unrelated ideas with balanced sensitivity for simplicity, productive engineering culture and particular habits hard-won over years of experience with the wrong design choices in other languages.

In the core of that is the design choice for relation between data and the methods which operate over it.

This article is not about learning techniques for writing functional code in Go or evaluating qualities in detail but more of a though flow on the way I perceive certain features of Go and how they relate to my understanding of functional programming. As consequence, there are no code examples but just some brain-food for further pondering and discussion.

Functional programming

We could define functional programming as programming approach which uses function definition, function composition and function application as primary tools for both representing data and modelling computation process over the data. There is much more than that, but for the purpose of the article and scope of functional aspects that Go can satisfy it would be enough.

Functions as first-class citizens

Go functions are first-class citizens in the language. It means that they can be treated as value, passed as arguments and returned as values from other functions.

Higher order functions

Functions being first-class citizens means that we can build so called higher-order functions which would receive other function as argument and return a function as result. This way we are already pretty equipped to use knowledge from functional languages which have build beautiful and useful abstractions on top of this concept.

Go doesn’t provide parametric polymorphism so that the whole power of higher-order functions can be put into use, but still there are many ways to use higher-order functions in practical way. Composing middleware functions in the net/http is one the common examples.

Functions as closures

Other functional aspect of Go is that functions have so-called lexical closure property. It means that they can access the variables defined in the scope which textually contains the function definition.

This allows functions to have access to some “state” which they carry with them once they are stored in a variable. This can be helpful in many cases and is one of the core tools of functional programming.

Method receivers and method attachment

Go allows functions to be “attached” to a named type making the relation between data and functions closer to what you would expect from an object-oriented approach with object/method relation. In the same time attaching a method to a receiver feels somewhat like functional programming with a bit of syntactic sugar on top.

There is no this as a concept. Methods do not have access to some object scope. Methods actually get the receiver as the first argument to the attached method. It is just written on the left side of the function signature. This is what functional programming looks like. Data is separate and operations on data receive the object of manipulation as the first argument.

One caveat is that methods attached to a named type in Go are not visible as top level definitions and can be accessed only by calling them through the receiver. On the score card, this gives on plus to the object-oriented way of looking at things.

Methods and data mutability

In most of cases idiomatic Go code is written in a way that there is mutable manipulation of the receiver. It is possible though to receive a copy of the receiver in the attached method and as well return copy of it, both by not using references but values. This provides ability to make a pure function with immutable data.

My take-away

Go, its philosophy and community are unique in many ways. One of them is that its design brings a valuable combination of, I would call it, statement oriented and functional programming in a way which doesn’t feel imposed or artificial but more symbiotic and beneficial to the language user.

Further reading