Susana Garcia
Elixir vs Go - Developer Experience
#elixir#go#dx

Elixir vs Go - Developer Experience

As developers, we tend to write as less code as possible. Elixir (and most functional programming languages) needs less code than others like Go. So we can say that Go is more verbose than Elixir.

But let’s talk about some of the interesting features of each language.

Elixir

Pattern Matching

Some of the problems Elixir solves is the “if-else hell” you find in most languages, just by using the so called pattern matching or other control flows apart from if, like case-do-end, cond-do-end and with-do-end.

Pipe Operator

Another popular feature is the pipe operator (|>) that allows to chain different functions together without the need to repeatedly reassign new variables.

With the pipe operator instead of having this code:

Enum.join(Enum.map(String.split("hello, world!!", " "), &String.capitalize/1), " ")

You can have this more readable one (the previous is still valid, though):

"hello world!!"
|> String.split(" ")
|> Enum.map(&String.capitalize/1)
|> Enum.join

Immutability

As we talk about the pipe operator, it’s probably time to mention one of the trickiest parts of Elixir, its immutability. One advantage of immutable languages is that it leads to clearer code. You can freely pass data around with the guarantee that no one will mutate it in memory.

For example, when we write:

x = 3
x = 6

Every time we “reassign a new value” to a variable, this variable “is rebinding”. The old value (3) stays in the same memory location (until the garbage collector comes in place or it’s out of scope), but its label or variable points to another memory location. To avoid rebindings when transforming data, Elixir provides the pipe operator that I’ve mentioned earlier.

Lists

Another interesting feature is how Elixir provide numerous ways to process list of data easily and lazily with comprehensions, enumerable and streaming functions, apart from having lists and maps like most others.

Doctests

One of my favourite features concerning testing is doctests. They mention that “Elixir makes documentation a first-class citizen” and I couldn’t agree more. Thanks to them, you can write actual tests in the documentation (apart from your other unit, acceptance, integration or performance tests). Their goal is not to replace tests, but to provide a way to have up to date documentation. Elixir’s test framework ExUnit includes everything you need to easily and thoroughly test your code.

The Not So Good

And last but not least, I’d like to mention some of the not so good things I found when using Elixir:

  • Debugging is not as easy as with other languages. In Elixir concurrency is key and makes it more difficult to find the source of specific problems.
  • Another important thing to have into account is that Elixir is not as popular as Go: sometimes you can’t find a specific library for something you need and, even though, you might have it in Erlang and can use it directly in your Elixir code, Erlang’s documentation is very hard to follow at the beginning.
  • There’re some concepts that might be hard to understand at the beginning, for example, some topics related to meta-programming like quote/unquote or macros.

Go

Simplicity

Go compiles quickly to machine code and yet has the convenience of garbage collection.

Although Go is a very C-like programming language, it’s very enjoyable to use, maybe because of its simplicity.

Cloud Software

Go fits really well with general performance-oriented cloud software and there are many DevOps open-source tools written with it, like Kubernetes, Docker and InfluxDB. That’s probably the main reason why in Helio we chose to work with it.

Functions

And even though Go is not a functional language, functions in Go are first-class citizens: it supports closures, multiple return values from a function and more.

Concurrency

Apart from that, in Go is easy to handle concurrency with goroutines and channels: goroutines are lightweight computation threads (different from operating system threads) and channels are the way how how goroutines should communicate. Go also includes a race condition detector mode to facilitate finding unsynchronised shared access.

To invoke a function in a goroutine, you just use the keyword go:

func hello() {
fmt.Println("hello world!!")
}
func main() {
go hello()
}

Formating And Testing

Go solves the debate of source code format with gofmt: the tool reformats your code and has no options.

In addition, Go has an amazing test framework in its standard library and it supports benchmarks and parallel testing (among others).

The Not So Good

To finish talking about Go’s development experience, I’d like to mention some of the not so good parts:

  • Types implement interfaces implicitly (not explicitly) and therefore it’s difficult to tell from looking at a struct (for example), whether or not it implements an interface (you can only really know by attempting to compile the program and that’s confusing).
  • Error handling and its repeatable code.
  • The lack of function overloading and enums (it has something called iota, but seems more like a workaround than a feature).