Erlang: Dispatching Closures (aka: why we don’t do OOP)

Oh my! It’s like “Creating nouns in the kingdom of verbs“!
(The link above is to a great post from Steve Yegge — read it and come back if you aren’t already familiar with it.)

A video talk-through of the code below and a bit of discussion

I wrote a funny little module today to demonstrate to a friend why FP folks sometimes refer to OOP objects as “dispatching closures” and occasionally make quips like “objects are a poor man’s closures”. I thought it would be fun to share it and also wanted a reference page to which I can refer people who have questions because this comes up every so often when a new OOP programmer starts pondering the nature of the universe after reading some introductory FP material.

Quite a lot of extra functionality could be added to this, such as distinguishing between functions that update the state of Self and functions that don’t to alleviate the annoyance of having to ignore the NewSelf return element of any “methods” beyond the pure built-in ‘get’, but we can leave that for another day as I really don’t expect anyone in their right mind would use the module below in real world code — it just introduces complexity, mental overhead, and potential weirdness if you pass an object between two different processes (send it to a process on another node and boom!), and in Erlang funs really shouldn’t be all that long-lived anyway.

In the below code a syntactic rewrite is needed to understand how to call the methods:

Pseudo PythonOOPsy Pseudo Erlang
class MyClass:
# stuff
MyClass = class(Data, Methods)
my_object.data_elementMyObject({get, DataElement})
my_object.data_element = valueMyObject({set, DataElement, Value})
my_object.do_something()MyObject({do, Something})
my_object.update(stuff).do_something(){ok, NextObject} = MyObject({do, update, Stuff}),
NextObject({do, something})

Here is the definition bit of the code, there is also a slightly more extensive and commented snippet on GitLab that has an additional demo/0 function that demonstrates one way dispatching closures can be called, manipulated, and extended through inheritance:

-author("Craig Everett <>").
-export([class/2, class/3, demo/0]).

        {data    = #{},
         methods = #{}}).

class(Defaults, Methods) ->
        (data) ->
        (methods) ->
        (Data) when is_map(Data) ->
            State = #s{data = maps:merge(Defaults, Data), methods = Methods},
        ({subclass, NewDefaults, NewMethods}) ->
            class(maps:merge(Defaults, NewDefaults), maps:merge(Methods, NewMethods))

class(Inherits, Defaults, Methods) ->
    Inherits({subclass, Defaults, Methods}).

object(State = #s{data = Data, methods = Methods}) ->
        ({get, Label}) ->
            maps:get(Label, Data);
        ({set, Label, Value}) ->
            NewData = maps:put(Label, Value, Data),
            object(State#s{data = NewData});
        ({add, Label, Method}) ->
            NewMethods = maps:put(Label, Method, Methods),
            object(State#s{methods = NewMethods});
        ({do, Label}) ->
            F = maps:get(Label, Methods),
        ({do, Label, Args}) ->
            F = maps:get(Label, Methods),
            F(object(State), Args);
        (data) ->
        (methods) ->

While this is an interesting construct, it is absolutely insane that anyone thought it was so utterly and all-encompassingly important that an entire new syntax should be developed just to hide the mechanics of it, and further, than entire languages should be created that enforce that this is the One True Way and impose it on the programmer. It’s cool, but not that cool.


PaaS with Elixir at Heroku

Heroku provides services and tools to build, run, and scale web applications. They enable developers and teams to focus on the design and craft of their apps. Heroku started development back in 2007, focused on the Ruby programming language, and since then, they have expanded to support multiple runtimes, either officially or via buildpacks.

As the platform grew, their engineering teams also adopted different languages, one of them being Elixir. In this article, we will talk about how two distinct engineering teams at Heroku, the Front-end team and the Vault team, have adopted Elixir.


First steps with Elixir

The Vault team was first to use Elixir inside Heroku. Their team is responsible for licensing and financial services, such as invoicing, credit card payments, etc. Most of their services are used internally at Heroku.

They had to rewrite one of their existing services and that was the perfect occasion to give Elixir a try, since the difficulties and risks with the service were mostly known. The experiment was a success: they deployed and ran their first Elixir application in production. This paved the way to use Elixir more and more.

Later on, they had a new challenge: they had to audit a large amount of data, and they knew from experience that the Ruby implementation would take too long to finish. Given they were already ramping up their familiarity with Elixir, they chose to apply Elixir’s GenStage to the problem, which is a low-level library for data processing, and that took only a couple hours. From this moment on, they were sold on the language and the platform.

Tackling operational complexity with Elixir

The Front-end team shares a similar story: they first used Elixir to solve a well-understood problem and took it forward from there.

The Front-end engineers are responsible for maintaining all user interfaces: the CLI, the dashboard, and a bunch of backend services that work with data. One of the features they provide to Heroku customers is analytics.

At first, they were sending their analytics to Mixpanel. However, they had some issues fetching the data, due to cross-domain concerns, and they decided to replace Mixpanel by an in-house Elixir service. The service used Plug, a library for building web applications, and had a single endpoint.

We were having a lot of fun and a lot of luck with it, so we kept doing it.

— Micah Woods, Lead Engineer, on migrating to Elixir.

They later spent most of a year focused on operational stability, and during this period, they started rewriting part of their Node.js microservices into Elixir. Today they have migrated their numerous Node.js microservices into one main Elixir application with one auxiliary service for authentication. The fact that Elixir was capable of handling everything they threw at it alongside their experience with Erlang’s stability - Heroku’s router uses Erlang - allowed them to simplify their operations considerably.

Productivity and scalability

The Front-end team has been using Elixir for two years. The team has 21 engineers: about 4 of them doing Elixir full-time, and 8 engineers altogether doing Elixir here and there.

The first service that they built with Elixir, the analytics services, receives requests and puts them into an in-memory queue to be processed within the same VM. It handles about 3k to 4k requests per second. 99% of the response times stay within 0-1ms, occasionally 4ms. They use 3 Heroku dynos for fault-tolerance - of course, Heroku uses Heroku for their own infrastructure.

The main Elixir application uses the Phoenix web framework to power the Heroku Dashboard, provide real-time functionality via WebSockets, and support other services. This application runs on 5 Heroku dynos - although their engineering team believes they could probably do with less. Memory consumption is also on the lower side: their biggest dyno uses 256MB.

The Vault team doing Elixir is only three engineers. Most of their apps are used internally, so they are generally not worried about performance. They continue using Elixir because they feel productive and happy with it. They have also found it is an easier language to maintain compared to their previous experiences.

On Phoenix

Both teams generally use Phoenix for web applications, unless they have a reason not to, which is rare. They acknowledge there is not a performance penalty for using Phoenix and you get a lot out of the box. Phoenix makes it easy to opt-in on the pieces that they need and remove the parts that they don’t want.

They have also found it easier to understand how Phoenix itself works under the hood, especially compared to their previous experiences with other frameworks, such as Ruby on Rails. This knowledge is consistently helping them maintain and update their applications as time passes.

Learning Elixir and growing the team

The growth of both Elixir teams has been mostly organic. Given there are multiple languages in their stack, they often hire for one or another language in particular, and not specifically for Elixir. If the new team members gravitate towards Elixir, they are further encouraged to explore and learn the language. They are also active practitioners of pair programming, so there are many opportunities in their team to learn from each other, rotate pairs, swap projects, and so on.

According to Matthew Peck, “the paradigm shift from Object-Oriented languages to Functional Programming was our biggest challenge when first learning Elixir”. However, the team agrees the investment was worth it: “Learning Elixir has made us better programmers. We have found that immutability made our code more readable, easier to test, and simpler to make concurrent. Now when we go back to an Object-Oriented language, we are thinking about how we can apply the same concepts there” - said Mike Hagerdon.

Amanda Dolan added some remarks on Elixir’s capabilities for writing concurrent and fault-tolerant applications: “One other challenge when learning Elixir is fully grasping concurrency and the Erlang/OTP patterns”. Some of them felt it took longer to master those concepts than they first expected.

Taylor Mock has his take on the challenges teams may face when adopting Elixir: “Another difference between Elixir and our previous stacks, Ruby and Node.js, is in the ecosystems”. They were initially concerned that the Elixir ecosystem would lack when it comes to third-party tools, but that was not what they saw. Taylor continues: “We found out that we can get really far with the concepts and mechanisms that the language itself provides. This shift can be scary, but we are now past it, and we find ourselves with leaner applications and fewer dependencies”.

Overall, both teams found the language itself quite approachable. Given they started with a small proof of concept, they were able to tackle their concerns in regards to adoption, development, and deployment as they moved forward. Historically Heroku also has had much success with Erlang, and that has contributed to the success adopting Elixir has seen inside Heroku.


Erlang: [video] The GUI experience on Windows with ZX and Vapor

I’ve written and spoken a bit about how ZX makes it easy to create, distribute and launch templated GUI projects in Erlang. I also showed how the (still ugly) GUI program launcher Vapor can make it easy for non-technical desktop users to use those programs.

So far I’ve been doing my examples on Linux where everything works in a familiar way and the terrain is fairly well known to us as developers, so in this video I want to show a bit about how things feel to Windows users who run client-side Erlang via Vapor using the standard Erlang runtime installation provided by Erlang Solutions and point out a few things that I find significant or can be improved in the experience.

If you have any insights into the issues highlighted in the video or have ideas about cross platform client development in general please let me know!


The sound of Erlang: How to use Erlang as an instrument.

The sound of Erlang

When most people think of Erlang, they think of enormous business platforms handling high volumes of concurrent users. That’s often accurate, but we wanted to show off a less conventional but fun project that you can do in Erlang. Sam Aaron’s Sonic Pi is a wonderful code-based music creation and performance tool. It’s a brilliant innovation that makes use of Erlang, and it could be a big part of the future of music. You can see an example of what Sonic Pi is capable of here. Also, you can join us at Code Mesh V conferecne on 5-6 November for Sam’s tutorial - Introduction to Live Coding Music With Sonic Pi.

We were inspired by this and decided to go one step further and show off how you can code music in Erlang directly.


This is a table of all the software used with their versions.

load testing diagram

The theory behind the music

Before we make music with Erlang, it’s worth explaining some of the underlying theory behind what sound actually is; this will come in handy later.

Sound is a vibration that propagates as an acoustic wave.

Frequency is the number of occurrences of a repeating event per unit of time. Its basic unit is Hz which is the number of occurrences per second.

The period is the duration of time of one cycle in a repeating event. The period is the reciprocal of the frequency.



  • f is a frequency in Hz
  • T is a period in seconds

The simplest way of generating a wave is by providing a sine wave with the given frequency.

Computers work in a discrete domain while sine waves work in a continuous domain; therefore, sampling is used to convert material from a continuous domain to a discrete one.

Sampling sound is the process of converting a sound wave into a signal.

alt text

The more samples we approximate, the better our sound quality will be, but this will also make the file in which we store the approximations larger.

If you want to learn more about sampling I recommend watching this video.

Starting a project

Start by creating a new project.
Since the script is small, I will use the escript template:

rebar3 new escript the_sound_of_erlang

Let’s check if the project worked correctly:

cd the_sound_of_erlang
rebar3 escriptize
mkdir out

If everything has gone to plan, then you should see the following output:

$ rebar3 escriptize
===> Verifying dependencies...
===> Compiling the_sound_of_erlang
===> Building escript...

$ ./_build/default/bin/the_sound_of_erlang
Args: []

Well done!

First wave

Lets now generate an example wave. To do this we can just:

wave() ->
    [math:sin(X) || X <- lists:seq(1, 48000)].

To save a generated wave, we need to transform a list of floats to binary representation and write this binary to a file.

save(Filename, Wave) ->
    Content = lists:foldl(
        fun(Elem, Acc) ->
            <<Acc/binary, Elem/float>> end,
        <<"">>, Wave),
    ok = file:write_file(Filename, Content).

Call the above 2 functions in the main/1 function as follows:

main(_) ->
    Wave = wave(),
    save("out/first_wave.raw", Wave),

Build the script binary with:

rebar3 escriptize

And run it with:


A new file called out/first_wave.raw should show up in a repository. Then use ffplay to listen to the result:

ffplay -f f64be -ar 48000 out/first_wave.raw

The options given are:

  • -f f64be means that input format is 64-bit big-endian float
  • -ar 48000 means that the input audio sampling rate is 48000 per second

You can listen to the result in the video below.

It’s not a pleasant sound (yet), but it’s a start.

For the sake of convenience, let’s try to play the resulting sound from the script:

play(Filename) ->
    Cmd = "ffplay -f f64be -ar 48000 " ++ Filename,

and add it to the end of main/1:

main(_) ->
    Wave = wave(),
    Filename = "out/first_wave.raw",
    save(Filename, Wave),

Now we can recompile the script and run it with:

rebar3 escriptize && ./_build/default/bin/the_sound_of_erlang

We can also convert a raw file to a .mp3 format with:

ffmpeg -f f64be -ar 48000 -i out/first_wave.raw out/first_wave.mp3

That will enable it to be played with any music player.

We have managed to generate a wave, save it to a file and play it.

Tuning in

Now that we can make sound let’s improve our wave, so it’s not just any random sound, but a fixed frequency for a given amount of time.

To have several samples played in a given amount of time, we need to multiply the sample rate times and sound duration, since the number of samples is an integer we should round the multiplication result.

NumberOfSamples = round(SampleRate * Duration)

The sinus period is 2 * PI, because we know that and the sample rate, we can calculate how long each signal step will last for a given frequency Hz.

Step = Hz * 2 * math:pi() / SampleRate

Knowing the number of samples and the Step, we can map the time domain to a signal as follows:

frequency(Hz, Duration, SampleRate) ->
    Signals = lists:seq(1, round(SampleRate * Duration)),
    Step = Hz * 2 * math:pi() / SampleRate,
    [ math:sin(Step * Signal) || Signal <- Signals ].

Let’s now modify a wave/0 function to get a sound of 440 Hz played for 2 seconds with a sampling rate of 48000 samples per second:

wave() ->
    frequency(440, 2, 48000).

I will change the Filename in the main/1 function to

Filename = "out/2Sec440Hz.raw",

Just add it to a repository.

You can listen to the result below.

Let’s play the new sound and compare it with the same frequency of sound from YouTube.
To me, they sound identical.

rebar3 escriptize && ./_build/default/bin/the_sound_of_erlang

We can now try playing two sounds with different frequencies and lengths of times, but we need to flatten the list of signals to make a list of signals from a list of lists of signals.

Filename = "out/2Sec440HzAnd1Sec500Hz.raw",

wave() ->
        frequency(440, 2, 48000)
      , frequency(500, 1, 48000)

You can listen to the result below.

Frequency to note

We can play a given frequency for a given amount of time, but how do we make music out of that?

From here we can see that the frequency of an A4 note is 440 Hz which is also known as pitch standard.
We can also lookup all other needed notes in the same way, but there is an alternative called the frequency ratio of a semitone, and it is equal to the twelfth root of two 2 ** (1 / 12).
To calculate A#4 which is 1 semitone higher than A4 we just multiply 440 * (2 ** (1/12)) = 466.16 and, after comparing it to the table the value is A#4 the correct corresponding frequency.

Let’s try translating the maths into code:

At the top of the file just below the module directive, we can add a macro for pitch standard and extract the sampling rate.


-define(PITCH_STANDARD, 440.0).
-define(SAMPLE_RATE, 48000).


Now we need to use the macro for the sampling rate in the code.
Let’s modify frequency/3 to frequency/2:

frequency(Hz, Duration) ->
    Signals = lists:seq(1, round(?SAMPLE_RATE * Duration)),
    Step = Hz * 2 * math:pi() / ?SAMPLE_RATE,
    [ math:sin(Step * Signal) || Signal <- Signals ].

Do not forget to change the calls of the frequency function by removing the last argument:

wave() ->
        frequency(440, 2)
      , frequency(500, 1)

and play/1 as follows:

play(Filename) ->
    StrRate = integer_to_list(?SAMPLE_RATE),
    Cmd = "ffplay -f f64be -ar " ++ StrRate ++ " " ++ Filename,

The following function takes the number of semitones to be shifted and returns a frequency of a shifted sound:

get_tone(Semitones) ->
    TwelfthRootOfTwo = math:pow(2, 1.0 / 12.0),
    ?PITCH_STANDARD * math:pow(TwelfthRootOfTwo, Semitones).

We need to introduce one more concept, beats per minute, which is the base time unit for a note to be played.
Each note is played in a given number of beats and the number of beats per minute is fixed, so we can calculate how long each beat lasts (in seconds) by dividing 60 by beats per minute.

Let’s introduce a new function for that:

beats_per_minute() ->

beat_duration() ->
    60 / beats_per_minute().

We can generate notes for a given amount of time with the following function:

sound(SemitonesShift, Beats) ->
    frequency(get_tone(SemitonesShift), Beats * beat_duration()).

Let’s try it out by providing the following wave:

wave() ->
       sound(SemiTone, 1) || SemiTone <- lists:seq(0, 11)

And play it by recompiling and running the script.
I saved my output as "out/increasingSemitones.raw".

You can listen to the below.

I only need some of these notes to play my songs, but you might need more, so I provided them in the semitones_shift/1 function. Let’s provide a helper function for easier sound notation:

note(Note) ->
    SemitonesShift = semitones_shift(Note),

semitones_shift(c4) -> -9;
semitones_shift(c4sharp) -> -8;
semitones_shift(d4flat) -> -8;
semitones_shift(d4) -> -7;
semitones_shift(d4sharp) -> -6;
semitones_shift(e4flat) -> -6;
semitones_shift(e4) -> -5;
semitones_shift(f4) -> -4;
semitones_shift(f4sharp) -> -3;
semitones_shift(g4flat) -> -3;
semitones_shift(g4) -> -2;
semitones_shift(g4sharp) -> -1;
semitones_shift(a4flat) -> -1;
semitones_shift(a4) -> 0;
semitones_shift(a4sharp) -> 1;
semitones_shift(b4flat) -> 1;
semitones_shift(b4) -> 2;
semitones_shift(c5) -> 3;
semitones_shift(c5sharp) -> 4;
semitones_shift(d5flat) -> 4;
semitones_shift(d5) -> 5;
semitones_shift(d5sharp) -> 6;
semitones_shift(e5flat) -> 6;
semitones_shift(e5) -> 7;
semitones_shift(f5) -> 8;
semitones_shift(f5sharp) -> 9;
semitones_shift(g5flat) -> 9;
semitones_shift(g5) -> 10;
semitones_shift(g5sharp) -> 11;
semitones_shift(a5flat) -> 11;
semitones_shift(a5) -> 12.

and modify slightly the sound/2 function as follows:

sound(Note, Beats) ->
   frequency(note(Note), Beats * beat_duration()).

To use the more convenient, newly created note/1 function instead of get_tone/1.
Now we can try out the sounds played by modifying the wave/0 function as follow:

wave() ->
       sound(Note, 1) || Note <- [
           c4, c4sharp, d4flat, d4, d4sharp, e4flat,
           e4, f4, f4sharp,g4flat, g4, g4sharp,
           a4flat, a4, a4sharp, b4flat, b4

I will save the result in "out/increasingNotes.raw" file.

You can listen to the result below.


When you listen to the increasing notes, you will notice that there is a very strange tick or thudding sound as the note changes.

This is because the sound increases and decreases too rapidly.
To resolve this issue, we can implement ADSR which stands for *A*ttack *D*ecay *S*ustain *R*elease and works by modifying the sound amplitude (volume) according to the following chart:

alt text

For the sake of simplicity, it is enough to only implement the Attack and Release components because we already have the Sustain part.
To implement the Attack, we will consider a sequence of numbers smaller or equal to 1 that will be generated in the following way:

attack(Len) ->
    [min(Multi / 1000, 1) || Multi <- lists:seq(1, Len)].

An example of this kind of list may look like this:

[0.001, 0.002, ... 0.999, 1, 1, 1, ..., 1]

We can also generate the Release a lazy way:

release(Len) ->

The release function generates the following list:

[1, 1, 1, ..., 1, 0.999, ..., 0.002, 0.001]

Now we need to slightly modify the frequency/2 to adjust the sound volume:

frequency(Hz, Duration) ->
    Signals = lists:seq(1, round(?SAMPLE_RATE * Duration)),
    Step = Hz * 2 * math:pi() / ?SAMPLE_RATE,
    RawOutput = [ math:sin(Step * Signal) || Signal <- Signals ],
    OutputLen = length(RawOutput),
        fun(Attack, Release, Out) -> Attack * Release * Out end,
        attack(OutputLen), release(OutputLen), RawOutput).

I saved the result as out/increasingNotesASR.raw. Now when you rebuild and run the script, you will hear a smooth transition as we pass between the notes.

You can listen to the result here.

Get ready to face the music

Now we will try to play the actual song.
Let’s modify the wave/0 function as follows:

wave() ->
    sound(f4, 0.5)  
    ,   sound(d4, 0.5)  
    ,   sound(d4, 0.5)  
    ,   sound(d4, 0.5)  
    ,   sound(g4, 2)  
    ,   sound(d5, 2)  
    ,   sound(c4, 0.5)  
    ,   sound(b4, 0.5)  
    ,   sound(a4, 0.5)  
    ,   sound(g5, 2)  
    ,   sound(d5, 1)  
    ,   sound(c4, 0.5)  
    ,   sound(b4, 0.5)  
    ,   sound(a4, 0.5)  
    ,   sound(g5, 2)  
    ,   sound(d5, 1)  
    ,   sound(c4, 0.5)  
    ,   sound(b4, 0.5)  
    ,   sound(c4, 0.5)  
    ,   sound(a4, 2)  
    ,   sound(d4, 1)  
    ,   sound(d4, 0.5)  

Also, change the beat per minute to 120. The reasoning behind setting given beats per second can be found here but it is out of the scope of this article so I will not go into further details.

beats_per_minute() -> 120.

You can recompile and run the script or just listen to the result below.

The result can be saved to out/StarErlang.raw. I hope you recognize the melody I picked, it is the Star Wars Theme.
Last but not least, let’s introduce an Erlang behaviour for a melody.
Create a new file src/melody.erl and define a melody behaviour.


-type note() :: c4 | c4sharp | d4flat | d4 | d4sharp | e4flat | e4 |
                f4 | f4sharp | g4flat | g4 | g4sharp | a4flat | a4 |
                a4sharp | b4flat | b4 | c5 | c5sharp | d5flat | d5 |
                d5sharp | e5flat | e5 | f5 | f5sharp | g5flat | g5 |
                g5sharp | a5flat | a5.
-type duration() :: float().

-callback beats_per_minute() -> non_neg_integer().

-callback sounds() -> {note(), duration()}.

Each melody may have different beats per minute, so this is the first function is needed to describe a song and the second function is the notes the song consists of.

There are two types to be introduced: note() which is one of the possible notes (aka sound frequencies) and the duration() which is a float saying how many beats the sound of a given frequency will last.

To use a song defined in a different module with a slightly simplified notation, let’s add a new macro which will store the module name in which the song is defined:

-define(SONG, star_wars_main_theme).  

and modify wave/0 and beats_per_minute/0 functions to use it:

beats_per_minute() ->

wave() ->
    RawSounds = ?SONG:sounds(),
    Sounds = lists:map(
        fun({Note, Duration}) ->
            sound(Note, Duration)
        end, RawSounds),

This will not work yet as there is no star_wars_main_theme module defined, so
create a file src/songs/star_wars_main_theme.erl and implement the melody behavior:



-export([sounds/0, beats_per_minute/0]).

beats_per_minute() ->

sounds() ->
    ,   {d4, 0.5}
    ,   {d4, 0.5}
    ,   {d4, 0.5}
    ,   {g4, 2}
    ,   {d5, 2}
    ,   {c4, 0.5}
    ,   {b4, 0.5}
    ,   {a4, 0.5}
    ,   {g5, 2}
    ,   {d5, 1}
    ,   {c4, 0.5}
    ,   {b4, 0.5}
    ,   {a4, 0.5}
    ,   {g5, 2}
    ,   {d5, 1}
    ,   {c4, 0.5}
    ,   {b4, 0.5}
    ,   {c4, 0.5}
    ,   {a4, 2}
    ,   {d4, 1}
    ,   {d4, 0.5}
    ,   {g4, 2}
    ,   {d5, 2}
    ,   {c4, 0.5}
    ,   {b4, 0.5}
    ,   {a4, 0.5}
    ,   {g5, 2}
    ,   {d5, 1}
    ,   {c4, 0.5}
    ,   {b4, 0.5}
    ,   {a4, 0.5}
    ,   {g5, 2}
    ,   {d5, 1}
    ,   {c4, 0.5}
    ,   {b4, 0.5}
    ,   {c4, 0.5}
    ,   {a4, 2}
    ,   {d4, 1}
    ,   {d4, 0.5}
    ,   {e4, 1.5}
    ,   {e4, 0.5}
    ,   {c4, 0.5}
    ,   {b4, 0.5}
    ,   {a4, 0.5}
    ,   {g4, 0.5}
    ,   {g4, 0.5}
    ,   {a4, 0.5}
    ,   {b4, 0.5}
    ,   {a4, 1}
    ,   {e4, 0.5}
    ,   {f4sharp, 1}
    ,   {d4, 1}
    ,   {d4, 0.5}

You can listen to the result below.

We have also provided a few more examples.

Sound of Silence

Super Mario Bros.

We hope you enjoyed this tutorial, If you want to see the full code go to we’re looking forward to hearing your examples of your favourite song, played in Erlang. You can share them with us on Twitter.

You may also like:

ElixirConf EU Virtual

MongooseIM - Open Source, scalable instant messaging

Online Erlang and Elixir training



Why Elixir is the Programming Language You Should Learn in 2020

What should you look for when choosing to learn a new programming language? The answer might vary depending on what your project or career goals are, but as a basic starting point, you want a language that:

  1. Is fun and easy to use
  2. Has the ability to meet modern user demands
  3. Has rewarding career progression
  4. Has an active and supportive community
  5. Has a range of helpful tooling
  6. Has frameworks to allow full-stack development
  7. Has easily accessible documentation
  8. Ensures you grow as a programmer.

Over the course of the article, we’ll show you how Elixir rates in relation to all of the above dot points. Before we start, it’s worth letting you know a little bit about me. I like to consider myself a polyglot developer. Over the years I have worked with Elm, Lua, Rust, Dart, Go, Kotlin, Scala, C, C++, Perl, Ruby, PHP, Python, Java, JavaScript, Erlang and Elixir. If I were to only pick one language to learn as a brand new developer in 2020, it would be Elixir.

Why Elixir is fun and easy to use

Fun might not be the only important consideration, but it is one that should not be underestimated. Elixir’s syntax shares a lot of similarities with Ruby; both are high-level, readable, fun programming languages. As a senior developer from Pinterest once said of his decision to transition to Elixir:
“"I thought, ‘Wow, it’s as fun as Ruby, but it has some chops — it actually performs really well.’”
Personally, I love the ability to write less code. It is a language that is expressive enough to be understandable and fast to read. For example, using the Tesla library, I could implement integration with a provider like Paypal in 20 lines of code. I will be happy to schedule a hands-on workshop or a 1-on-1 call if there is a need for dive into this library.

How Elixir has the ability to meet modern usage demands

We all heard of the digital transformation, right? As populations grow, and more services move online there is increased pressure on systems to be able to handle huge spikes with billions of concurrent users.
Elixir runs on the BEAM VM. A system built for concurrency. As a result, it can handle these user spikes with ease. For those interested in what concurrency is, and how it works, we have a blog comparing the JVM and BEAM VM, which explains it well.
Pinterest and Bleacher Report are just two of widely popular and big companies who moved to Elixir in order to help solve issues of scalability. In the case of Bleacher Report, one of our clients, they were able to move from 150 servers, down to 5 servers. Not only did they reduce there physical infrastructure requirements, they were also able to handle more traffic with faster times. A language that can be relied upon for scalable, fault-tolerant will always be sought out by high profile companies.

Elixir’s rewarding career progression

Elixir is a growing language which is being adopted at a significant rate. As a result, Elixir developers have the opportunity to work for wide spectrum of companies such as PepsiCo, Pinterest, Lonely Planet or MBTA. And the list is continuing to grow. Check out this list for companies added in the last 12 months alone.

The supportive and active Elixir community

As you might expect with any new technology or programming language, the Elixir community started out small.
Nowadays Elixir has a thriving community that continues to grow, with fantastic events such as ElixirConf in Europe and in the US, EMPEX, Code Elixir LDN and Gig City Elixir, as well as Meetups all around the world. This community means the language is continuing to grow and evolve with new tooling, and that there is always someone to provide inspiration or help find solutions to a problem.

Elixir’s range of useful tooling

Tooling makes languages more versatile and tasks easier. They mean that you don’t have to reinvent the wheel with every new use case. Elixir already has a number of powerful tools, including Phoenix LiveView which enables developers to build real-time, front-end web applications without writing a line of JavaScript or Crawly which makes web crawling and Data Scraping in Elixir easy. Not only does Elixir have a wide-variety of tooling available, but as we mentioned earlier, there is also a passionate community of people continually creating new tools and improving the ones available.

Elixir frameworks allow for full-stack development

Given its scalability performance and its origins in Erlang, it is no surprise that Elixir is a popular backend choice. As mentioned above, Phoenix LiveView has provided an easy, time-efficient and elegant way for Elixir developers to produce front-end applications. Finally, the Nerves framework allows for embedded software development on the hardware end. As a result, Elixir is a language that can be adopted right throughout the tech stack. This doesn’t just make it an attractive choice for businesses; it also opens up the door for where the language can take you as a developer.

Elixir’s easily accessible documentation

A language and community where documentation is valued is one that makes knowledge sharing easy. The high standard of documentation in the Elixir community makes it easier for you to learn the language, but also, make it easier for anyone to contribute to its improvement and growth.

Learning Elixir can make you a better programmer in other languages

There are a number of stories, from people who come from object-oriented languages, who found that the process of learning Elixir made them a better programmer in their language of choice. Learning a new purely functional programming paradigm forces you to examine the way you approach programming. In doing so, it can help you identify habits that can be improved and introduce you to new ways of thinking about problems. This fresh perspective is invaluable for all future development you do, regardless of what language you are working with. For our friends using Ruby, this is particularly true, as the Elixir syntax will be familiar to you, allowing you an easy transition into functional, concurrent programming.

Everyone will have different motivation and considerations for what programming language to choose. But these are the reasons I would recommend you learn Elixir in 2020, for the years ahead.

Ready to get started in Elixir?

There are many ways to start. First, you should head to the getting started page of the official Elixir website. We also offer a number of free downloadable packages for Elixir.
Secondly, we offer a variety of Elixir training, including our Elixir Programming Training For Beginners.
To get started in the community ElixirForum is a great place to start, as well as searching the #Elixirlang and #MyElixirStatus hashtags on Twitter.

You may also like:

ElixirConf EU Virtual

Online Elixir training

Our Elixir Services


OTP 23.1 Release

img src=

OTP 23.1

Erlang/OTP 23.1 is a the first maintenance patch release for OTP 23, with mostly bug fixes as well as a few improvements.

Vulnerability fix

A vulnerability in the httpd module (inets application) regarding directory traversal that was introduced in OTP 22.3.1 and corrected in OTP It was also introduced in OTP 23.0 and corrected in OTP 23.1 The vulnerability is registered as CVE-2020-25623.
The vulnerability is only exposed if the http server (httpd) in the inets application is used. The vulnerability makes it possible to read arbitrary files which the Erlang system has read access to with for example a specially prepared http request.

General build issues

Adjust /bin/sh to /system/bin/sh in scripts when installing on Android.
Changes in build system to make it build for macOS 11.0 with Apple Silicon. Also corrected execution of match specs to work on Apple Silicon.

Full list of bug fixes and improvements in the readme.

Download and doc

Pre built versions for Windows can be fetched here:

Online documentation can be browsed here:
The Erlang/OTP source can also be found at GitHub on the official Erlang repository,


Copyright © 2016, Planet Erlang. No rights reserved.
Planet Erlang is maintained by Proctor.