My first 5 Elixir d’ohs

Konstantin Zolotarev
4 min readApr 23, 2017

--

I started my developer carrier about 10 years ago and primary languages I used was PHP and Java. To be honest I was pretty happy with this stack and started to collaborate in OpenSource projects like Openfire and Spark. I felt in love with OSS and spent really lot of time working on it. As you could see my tech stack was 100% around OOP.

In 2011 I met Node.js and started working with it. Let’s be honest it populated OSS like nobody before. Node.js opened world of realtime applications and highload for me. As a result now 90% of my work is realtime/highload applications and communication services.

Recently I opened a magical world of Elixir/Erlang for me. and I could say that I really enjoy working with it. But since I started to learn Elixir after many years of PHP/Java/Node.js I faced with some “D’ohs”

1. Understanding of Processes

Process in Elixir is not same as process in OS or thread in Java. It was really hard to me to realize that almost everything in Elixir is made with processes. Need to open connection to WS client — create a new “process”. Need to write a lib — create a “process”. Need to create a logic that communicates with something — create a “Process” that consist of “process”.

And OTP and GenServer are your first friends in this. In most cases all you need to do is to spawn processes and make the communicate with each other.

2. Language flexibility and syntax

In Node.js after ES6 all JS developers started to simplify their code. Async functions were replaced with promises. Iterations replaced with map/reduce. And code became much more clear and simple. Arrow functions brought beauty into JS code.

someFunction(params, function (err, response) {
if (err) {
// do something
}
nextFunction(response, function () {
if (err) {
// do something
}
// etc...
})
})

Became something like:

someFunction(params)
.then(nextFunction)
.then((result) => { /* etc... */ })
.catch((err) => { /* do something */ })

Elixir provides much more flexibility in it’s syntax. Same code structure could be written in Elixir in this way:

result = some_function(params)
res = next_function(result)
# etc...

But it could be simplified with pipes:

result = params
|> some_function()
|> nextFunction()
# etc...

Why don’t we have error handling ? Hmmm… because everything is small tiny processes. Let it fail, die OTP knows how to handle it. It would be restarted. Main idea of code in Elixir — Let if fail. No I’m serious. Let it fail. Elixir has lot of tools to handle it and knows what should it do. Should process be restarted or die or kill set of other processes…

3. FOR loops and iterations

When we need to iterate something in OOP language we start with various iterators and for loops. Very simple example:

for (let i = 0; i < 8; i++) {
console.log(i)
}

In opposite in Elixir for loop exist but is not very common to use it. Elixir provides you with pipes and set of methods to make everything clear.

Enum.map 0..8, &(IO.puts &1)

Ok it’s looks more like a magic, but here we have pipe |> operator.

0..8
|> Enum.map(&IO.puts/1)

Much cleaner right ? In JS we also can one similar syntax. But to fill array with something — it’s really painful.

[0,1,2,3,4,5,6,7,8].map(console.log)

After some time working with Elixir I found out that it’s very similar to Node.js + Lodash/Underscore. Also I found this article that might be helpful for Node.js devs who want to start working with Elixir: http://www.east5th.co/blog/2017/04/17/who-needs-lodash-when-you-have-elixir/

4. Error handling

In Node.js world you have to handle all errors/exceptions that raises during application work. And if you did something wrong or forgot to handle exception — your application will die. In Elixir world in most cases you don’t need to think about such problems. Remember — “Everything is processes”.

If error will rise somewhere process will die and Supervisor will handle it. Of course there are cases when you need to handle error in code. And in that case language provides you with set of useful features. And when you use function you know what to wait form it.

For example Elixir has 2 functions that reads file. File.read and File.read! and both do same. Difference only in error reporting. File.read will return error to your code but File.read! will throw it to runtime.

case File.read "hello" do
{:ok, body} -> IO.puts "Success: #{body}"
{:error, reason} -> IO.puts "Error: #{reason}"
end

And exception might be thrown like:

iex> File.read! "unknown"
** (File.Error) could not read file unknown: no such file or directory
(elixir) lib/file.ex:305: File.read!/1

There are more ways to work with exceptions, process termination but you could read it in official docs.

5. Testing and mocks

When you need to test something in Node.js sinon is your first friend. Mocking services and methods are pretty common in this language. Same with PHP/Java.

But in Elixir you don’t need mocks. All you need is to define module/method in tests. I wouldn’t tell you a lot about this. Because Jose Valim (creator of Elixir language) wrote an awesome article about mocking in Elixir

http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/

--

--