Contents

Function Composition in Go

I’ve been playing with functional JavaScript for a while, especially partial applications and function composition, and I find those concepts are very helpful in my daily coding practices. At first, if you are short in mathematical logic systems (just like me), making sense anything functional could be a bit out of hand. Fortunately there are a lot of reading materials that do a great job explaining the concepts in an easy and pragmatic way. You could search those articles in medium and/or you would want to read this book.

Go, by design is quite different from JavaScript. It is an attempt to combine the ease of programming of an interpreted, dynamically typed language (e.g., JavaScript) with the efficiency and safety of a statically typed, compiled language. It also aims to be modern, with support for networked and multicore computing (Go FAQ page, 2016). However, despite the differences, there are some similarities between JavaScript and Go, one of them is that they treat functions as first class object, which means function is an object that can be passed around as function’s arguments or can be returned from other functions, and yes, function is a type in Go.

type fnString func(string) string

The effect of having first-class functions is that we can write Go in a more functional manner, just similar to JavaScript. In JavaScript we can easily write a compose function (function to do function composition) as the following.

const compose = (a, b) => (val) => a(b(val));

// variadic versions
const compose = (...functions) =>
  (value) => functions.reverse().reduce((acc, fn) => fn(acc), value);

In Go, as it is a typed language, and it does not support generic type per se, first we need to define a type of what the compose function will work against.

type fnString func(string) string

Here, we define fnString of function type that receives a string argument and returns a string. Next, is to define the compose function itself.

func compose(a fnString, b fnString) fnString {
	return func(s string) string {
		return a(b(s))
	}
}

The compose code is pretty straight forward just like the JavaScript version. The only main difference is that the function has to comply with the given type, fnString in the example above. So if we want to compose functions that work on other than strings, we might need to define the appropriate type and the appropriate compose function (as Go does not have generic type), in contrast to the JavaScript version that will work for all type —JavaScript is a dynamic programming language after all.

Then, how about the variadic version? Fortunately, Go supports variadic arguments so it is easier to port the JavaScript code into Go. However, Go does not provide direct translation of JavaScript’s Array.reduce function, the only way to compose the functions is by iterating through the passed functions and compose them one into another.

func compose2(a fnString, b fnString) fnString {
	return func(s string) string {
		return a(b(s))
	}
}

func compose(fns ...fnString) fnString {
	return func(s string) string {
		var res fnString
		res = fns[0]
		for i := 1; i < len(fns); i++ {
			res = compose2(res, fns[i])
		}
		return res(s)
	}
}

As seen in the code, the functions are iterated by using a for statement. Another way to loop over the functions is by recursion (as mentioned by Whargharbl) in the comment section.

func composeRec(fns ...fnString) fnString {
	return func(s string) string {
		f := fns[0]
		fs := fns[1:len(fns)]

		if len(fns) == 1 {
			return f(s)
		}

		return f(composeRec(fs...)(s))
	}
}

The working example of the above function composition can be seen on https://play.golang.org/p/l0bKbeDQ8x

***

Even though writing Go code in a more functional style is possible, however, Go is not a functional programming language. Hence, I’m still not convinced that writing Go program in a functional manner entirely is a good approach (if that is even possible), but I might be wrong.

Update

[05/07/2017]: Added the recursive version of compose as Whargharbl mentioned.

Hello, I'm Nauval. I code for living. I blog in my spare time.

Have a look around and drop me an email should you have any questions, feedback or just to say hi!