It’s a bit surprising that higher-order functions like map and filter have not caught on in Go.

They seemed to have caught on quickly when they were added to Java. One of the long standing issues back then was the clunky and verbose approach to writing closures. Java 8 fixed this with the introduction of the lambda (the -> operator). Suddenly, what once took multiple lines of boilerplate could be done in a single expression. The underlying mechanism was still the same but the new syntax was enough to get people to use it (amongst other things, read on).

I don’t see that in Go. With generics in Go 1.18 reducing the need for interface{} and type assertions, I would have expected the tide to turn a little: more maps and filter functions, and way less for loops. But it hasn’t seem to happen yet. I still see those same for loops that I’ve been seeing over the last eight years.

I’m not sure of the reason but I can guess I could be explained by two things.

The first is Go’s culture. And yeah, you could describe Go as having a culture1. It’s one that’s quite conservative and methodical. Fancy ways of doing things that sacrifice readability in favour of terseness is usually frowned upon. It’s proper to make sure the code is clear, even if it takes more room on the screen.

The culture comes through in the design of the Go language itself. A classic example is the use of the error type rather than exceptions. And I think it partly explains why higher-order functions have not caught on. It’s not because you can’t do it. At least you had proper closures in the language, which is something you couldn’t say about Java, back in the pre-1.8 days.

But I don’t think culture is enough. You couldn’t say that using higher-order functions in Java 1.6 was a big thing back then either2. What got them moving so quickly?

This is where I think reason number two comes in, which is the lack of standard library support. When Java 8 came out, every collection type was retrofitted with a bunch of higher-order methods which made it trivial to map, filter or reduce anything you need. There was event a new streams package, allowing you to build pipelines that are nothing but higher-order methods. All of this was useful and fun to work with, and people naturally wanted to use them.

Nothing like this existed when Go 1.18 was release. Nothing like this is in the upcoming release of Go 1.20.

Now, to be fair, this is very characteristic of how Go maintainers add features. They take their time, making sure not to break backwards compatibility or locking themselves into a design that is difficult to evolve. And I understand the reasons for why they want to go slow here. But that means that an “official” package of higher-order functions will take time to be ready. And no such package exists now. Sure, there are open-source and experimental ones out there, but would you be using those for any production level code? Maybe adding one more for isn’t exciting, but at least it doesn’t involve another dependency (and you’re already using for loops in several of your other functions anyway).

So I guess I’ll need to wait a bit longer for higher-order functions to be more of a thing. I can’t say I’m not disappointed: one of nice things about working in a language like Ruby, JavaScript, or even Java itself, is all the higher-order functions they have. I’m still hopeful that they will come eventually. After all, generics are only a year old. And Go as a language may move slowly, but at least it’s still moving.

  1. Maybe another way to put it is a “way that things are done.” ↩︎

  2. Unless you’re writing Android apps, in which case you’re forced into a culture of anonymous classes for all the callbacks you need to write. ↩︎