But, is Go a functional language?

2016-05-01

Gopher

This question has come up a couple of times recently, and I'm often surprised that while most people would quickly agree that Scala and Clojure are functional languages, they often disagree on the case for Go. I think that one of the main reasons is that we associate many different things with functional programming. For example, you might hear the following concepts associated with functional programming:

  • Functions -- d'oh!
  • Immutability
  • Functions without side-effects
  • Recursion
  • Separation of data and logic
  • An advanced type system
  • Lazy evaluation semantics

Personally, a summary of the core concepts of functional programming by Martin Odersky works for me. He lists two points:

  1. Functions as first class citizens.
  2. Immutability

The first one allows me to decompose my application into small building blocks and easily share functionality. The second prevents me from quickly shooting my foot in a concurrent setting.

Looking at Go, the first one is straight-forward: Functions can be values that another function depends on or be produced by a function. Have a look here for an overdose of functions in Go.

The second one is a bit more tricky. Go's structs, arrays and slices embrace mutability. Rather than restricting mutability on data structures, Go by default restricts the scope of mutation. It implements call-by-value evaluation semantics, copying data when passed from one scope to another. Consider the following example for sharing a struct between functions:

package main

import "fmt"

type person struct {
  name string
}

func setName(p person, n string) {
  p.name = n
}

func main() {
  p := person{name: "Hans"}
  setName(p, "Peter")
  fmt.Println(p) // prints: {Hans}
}

Or more interestingly, this example for sharing data between routines via channels:

package main

import "fmt"
import "time"

type person struct {
  name string
}

func printer(input chan person) {
  p := <-input
  for {
    fmt.Println(p, "in printer") // prints: {"Hans"} in printer
    time.Sleep(1 * time.Second)
  }
}

func main() {
  persons := make(chan person)
  go printer(persons)

  a := person{name: "Hans"}
  persons <- a

  a.name = "Peter"
  fmt.Println(a, "in main") // prints: {"Peter"} in main

  time.Sleep(5 * time.Second)
}

To come back to the comparison with Scala and Clojure: Go restricts by scope where Scala and Clojure provide data structure implementations that internally prevent mutation. I could not say which approach is the better suited one. Both work for me and both have their pros and cons.

Similar to immutability in Scala and Clojure, there are ways around it if need be (I could share a pointer to the value instead of the value itself), but by default modifications are restricted to a well-defined scope.

Ultimately, I don't think Go aims to provide any language feature that one associates with functional programming in particular and I don't think that it should. Instead, it aims to be a general-purpose language with simple building blocks. For me, these blocks often enable me to write in a style that I consider functional, but more importantly with the goal of simplicity in mind.