A taste of Elixir, a concise and productive language

You’ve possibly heard of this language recently. Elixir has been gaining publicity as a new framework called Phoenix has been making exciting claims.

Phoenix is advertised as a “Productive, Reliable and Fast” web framework and is built with Elixir. Before getting too excited about Phoenix I wanted to dive into Elixir and see what this mysterious language has to offer.

First things first. These companies already use Elixir:

  • WhatsApp
  • Discord
  • Pinterest
  • Riot Games
  • Weedmaps

That peeked my interest!

What will I do with such a beautiful language

Today I want to show you some of the joys that Elixir provides through a small HackerRank problem.

We’ll cover, basic functions, iterating over collections, getting user input, pipes, pattern matching and guards.

Before we get started with the problem let’s install Elixir and make a project!

Getting started

Elixir has excellent instructions on getting up and running here!

Check that you’ve installed elixir properly by typing elixir -v into your terminal or command prompt:

Elixir comes with excellent tools.

  • Mix: Elixir’s build tool. It sets up your project, fetches dependencies and tests your code. It’s awesome.
  • IEx: An interactive repl for testing your code on the go. I’m always typing away in it. Open it with iex in your terminal/command prompt but we’ll get to that later.
  • ExUnit: A simple but powerful testing framework. We won’t touch this much in this post.

Let’s get to coding this problem!

If you’re looking for an IDE to do your Elixir coding in, I strongly recommend VSCode with the vscode-elixir plugin installed. It’s lightweight, responsive and has the goodies like syntax highlighting and autocompletion. Hint: View -> Integrated Terminal will open a terminal which you can then use Iex in.

Starting a project

Navigate to a directory where you’d like to place your project (which is the solution to this code problem). Once you’ve navigated to this folder in your terminal, make a new project with the command mix new hackerrank_sol. Of course you can call the project whatever you wish.

This creates a new folder with your Elixir project. Let’s open this folder in our favourite code editor. Our code will live inside the following file: lib/hackerrank_sol.ex. Check that everything works by running mix test.

Let’s code!

There is some code already in your lib/hackerrank_sol.ex file:

We’re going to leave this and start coding below it in a new module called Solution (you can have multiple modules in one file).

The question requires us to get user input. Specifically a list of numbers. Let’s open up Iex in our terminal and have a look for what we need.

Type iex into your terminal and you’ll be greeted by the friendly REPL (Read Evaluate Print Loop) for Elixir. Use h() often to find information on functions because the documentation is great.

Because we want to get IO from the user we will look at the IO module. List of modules here: https://hexdocs.pm/elixir/Kernel.html

In iex we can see what IO exports and also ask for help like so:

Holy moly, we get an example and other nice information! A quick note on the name IO.gets/2. The /2 just means that the function takes two arguments. Similarly /1 means one argument and/0 means zero arguments. The fancy name for this is the arity of the function. (Now you have another awesome party fact)

Let’s use this function in iex just to get a taste of what it returns.

Ok! We should really actually try to solve the question now!

Note: You can print to the console using IO.puts/1

Between Two Sets

This problem can be summarized as such:

  • We are given a line with the length of two arrays, a and b.
  • We are given an array a. It’s a list of integers.
  • We are given an array b. It’s also a list of integers.

An element x is said to be between a and b if:

  • All the elements in the array a are factors of x.
  • x is a factor of all the elements in b.

We then want to know how many different values of x there are.

Check out the concrete examples given by hackerrank to clear up what’s required.

Let’s divide this problem into three pieces:

  • Getting and processing the input.
  • Making sure x is a factor of all the elements in b.
  • Making sure all the elements in the array a are factors of x.

Getting and processing the input

This is really clean in Elixir. We have IO.gets/1 which gets a line of input from the standard input. We will be getting 3 lines of input in this problem, and each line is a list of integers. Let’s create a general getLine/0 function that gets a line from stdin and returns a list of numbers.

Here’s a naive implementation that should be quite easy to follow. Feel free to use iex to go through the lines and see what each line is doing.

If you want to test this function, fire up iex in your terminal using this command: iex -S mix. This command lets you access your mix project within iex. If you make changes to the code you can also use recompile() to recompile your work so you can play with the freshest code.

To test this function in iex we type Solution.getLine().

Before making this code more idiomatic, let’s have a look at some great things provided by elixir. We’ve got this @doc tag above the function. This is called a module attribute and it tells Elixir that you’ve provided documentation to the getLine/0 function. Type h(Solution.getLine/0) and observer your own documentation displayed in all its glory! (Notice that documentation supports markdown!)

We won’t dwell on this now, but you can write executable doctests in your documentation as well. Most of the time when you see examples in documentation they’re executable to make sure they’re valid. This allows for great separation of unit and integration testing.

Let’s quickly go through the funky looking line:

converted_to_int = Enum.map(split_line, fn el -> String.to_integer(el) end)

We are mapping over a list of strings, and converting each element into an integer. The following is happening:

["1", "10", "3"] -- Enum.map --> [1, 10, 3]

Each element is being passed into the anonymous function as el which is then converting that string into an integer.

The final line of a function is implicitly returned. I find this actually encourages clean coding as it’s very hard to write weird nested conditionals which return strangely.

Let’s clean this up so everyone is impressed using another awesome Elixir operator…

We’re going to trust that every string is a valid integer (but you shouldn’t in the real world).

The pipe operator

There is a pattern that has emerged in our function. Each line is taking the output of the function above it and putting it in as its first argument. Elixir gives us this cool operator: |> which pipes the output from the left to the right.

Check out the code without all the intermediate variables!

Notice we remove the first argument of each function because it’s being handled by the pipe operator.

That’s input done! Now let’s tackle making sure x is a factor of all the elements in b.

Making sure x is a factor of all the elements in b.

I’ll skip to the chase. We need to find out the greatest common divisor of the elements in the list b. Then if x divides into the lowest common divisor it satisfies the condition that it is a factor of all the elements of b.

Did you instantly scream EUCLID’S ALGORITHM? A quick google later for finding the greatest common divisor you stumble across this mathematical formula.

Straight from wikipedia

You also remember an imperative way of coding this algorithm (from python’s standard library):

def gcd(a, b):
"""Calculate the Greatest Common Divisor of a and b.
Unless b==0, the result will have the same sign as b (so that
when b is divided by it, the result comes out positive).
while b:
a, b = b, a%b
return a

We’re going to do even better and write our own in Elixir that uses the mathematical definition, provides great error messages and is still fast.

Guards and Pattern Matching

Elixir supports pattern matching as well as checking conditions on inputs to a function. Here’s a simple example of pattern matching:

What we’re saying here is that if the argument is 0 we want to run the top version of the function. If the argument is not 0 we’ll return it in a string.

Wait!!! We don’t want that third behaviour!

This works but unfortunately the argument x can be anything! Luckily we have guards which allow us to add constraints to the arguments.

We can improve this pattern matching example by adding is_integer guard like so:

Look what happens when we try to pass something invalid:

Taking our new guard for a test run.

We get a very helpful message that specifically shows where our argument failed. We can see that a list of [1,2,3] failed to be 0. It also failed to be an integer.

We’ve now got more than enough to write our gcd function.

There are more guards than code! (Psst, there is a way in Elixir to create your own combination guards, but that’s out of scope for today)

This also follows the mathematical definition of greatest common divisor! There is of course a faster way using remainders which I leave as an exercise to you. (solution)

We need to find the greatest common divisor of a whole list though…. To find the gcd of a list of numbers, we just need to reduce the numbers against a current greatest common divisor which we update with each element. I’ll use a diagram!

We start with this list:[8, 4, 16, 2]

Then we use gcd on the first two numbers:

Reducing -- List left to reduce
gcd(8, 4) -- [16, 2]
= 4 -- [16, 2]
gcd(4, 16) -- [2]
= 4 -- [2]
gcd(4, 2) -- []
= 2

There is a function for doing exactly this called Enum.reduce/2. This list reducing function can also be called gcd since it’s calculating the greatest common divisor.

Calculating the lowest common multiple

The lowest common multiple is much simpler now that we have a gcd function. In fact it’s the following formula:

lcm(a, b) = a × b / gcd(ab)

Damn that’s a great formula. We can very easily code this up, including the reducing as demonstrated.

The Finale

Now that we have a way to grab input, a way to find the greatest common divisor and a way to find the lowest common multiple we almost have a way to find the solution.

We’re going to find the solution in the most boring way ever. We’re going to “loop” over each integer from the lowest common multiple to the greatest common divisor checking that the number satisfies both conditions of the problem. We’ll then count the resulting x values that satisfy the tests.

In the function we’ll write, we’ll take in the highest value (or the gcd of list b). We’ll also take in the lowest value (or the lcm of list a).

We’re checking first that the gcd value from list b is indeed greater than the lcm value from list a (otherwise guards fail and we return 0).

lowest..highest is a range. The docs say this.

“Defines a range”

1..3 is basically [1,2,3] in the way we’re using it above. The code basically creates a range of all the possible values of x between the lowest and highest number, and then filters based on the two conditions. The first filter checks that x is a multiple of the lcm of list a. The second filter checks that x is a factor of the gcf of list b. Then we count the results 🙂

We can now put our pieces together to get the input, and calculate the result:

This puts it all together to solve the hackerrank problem! 🙂

Congratulations for reading this far! Hopefully I’ve shown you that Elixir is a pleasure to work in. The pattern matching and guard concepts allow you to enforce better preconditions regarding what your functions will take.

Pattern matching goes a lot farther than what’s shown here, and it really makes for readable, lovely code. Full solution to the hackerrank code here.

I haven’t covered how awesome the testing is in this language, or the fact that Elixir 1.6 comes with a code formatter. I didn’t even mention tail recursion or the actor model or how easy concurrent programming can be in this language.

Related posts

Leave a Reply

Be the First to Comment!

Notify of