Elixir Language Tutorial – Getting Up To Speed For a Rubyist

June 13, 2017

Elixir Language Tutorial – Getting Up To Speed For a Rubyist

Normally, I like to read a book about a new programming language and then try doing things with it. If you haven’t setup Elixir on your system, you can read this post for some help.

Elixir basics

Yet, when I first started learning Elixir, I plunged in by trying exercism.io exercises. Recently, I picked up a copy of the great Elixir in Action book by Manning.

Here’s a few things that I think would be great for anyone coming from Ruby to know to bring themselves up to speed on Elixir basics.

Elixir intrinsic properties

1 – The first thing to note is that data is immutable. However, you can still bind variables.

That is why you will see things like:

list = [1, 2, 3]

Now [1, 2, 3] is bound to list.

2 – Elixir is a dynamic language like Ruby.

3 – Elixir’s garbage collection is Erlang’s garbage collection. You can tune the GC for performance.

Modules & Functions

General Information

4 – A module is a namespaced collection of functions.

5 – You can have a condensed function form:

def rectangle_area(a, b), do: a * b

The elongated function form is:

def rectange_area(a, b) do
  a * b
end

Default Arguments and Arity

6 – \\ is used to specify a default argument value in a function

def sum(a, b \\ 0), do: a + b

7 – Function arity distinguishes functions of the same same, so you can’t have splat operator like in Ruby

Public vs Private, Importing, and Alias

8 – public vs private functions – private can’t be invoked outside the module

9 – importing modules: Importing a module allows you to call its public functions without prefixing them with the module name

10 – alias – This is useful for modules with long names.

defmodule MyModule do
  alias TimingModule, as: TimeIt

  def some_function do
    TimeIt.some_timing_func("calling time it")
  end
end

11 – import – Sometimes you want to call another module’s functions without prefixing a module name:

defmodule MyModule do
  import TimingModule

  def some_function do
    some_timing_func("calling time it")
  end
end

12 – module attributes – compile time constants that can be queried in runtime

defmodule Square do
  @moduledoc "Basic square functions"

  @side 2

  @doc "Computes the area of a square with side 2"
  def area do
    @side * @side
  end
end

Anonymous Functions

13 – Use a signature like rect.(5, 4) to call an anonymous first class function

rect = fn(w, h) ->
         w * h
       end

Documentation and help

14 – @moduledoc and @doc for documentation – Use these document a module and its functions as in 13 –

15 – h Square – This feature of iex will allow you to see the documentation for the Square module.

16 – ex_doc – used for generating HTMl docs

Piping Operator and Captures

17 – |> is called the pipeline operator (or sometimes the pipe operator)

18 – You can have a more compact lambda notation. This is through the use of the capture(&) operator.

Enum.each([1, 2, 3], &IO.puts/1)

19 – The capture operator enables you to write compact lambda notation:

volume = fn(x, y, z) -> x * y * z end

  # can also be written as:

  volume = &(&1 * &2 * &3)

  # call volume with this syntax:

  volume.(1, 2, 3)
  # => 6

Other Tips

20 – There’s no clear cut maximum integer size as there are no limits on integer size – check erlang memory guide – http://erlang.org/doc/efficiency_guide/advanced.html#id68923

21 – You can have atom constants without a beginning colon

MyAtom == :"Elixir.MyAtom"
# => true

22 – There are no booleans in Elixir, just :true and :false (can be referenced as true or false)

23 – nil is still falsy

24 – Tuples such as {3, “Bob”} are considered untyped structures

25 – To do list concatenation and removal use the ++ and operators

[1, 2] ++ [3]
# => [1, 2, 3]
[1, 2, 3] -- [3]
# => [1, 2]

26 – Like Ruby, you can use #{} to place an expression in a string

Enumerable

27 – Enumerable.each/2 takes an enumerable argument (e.g., lists) and a one-arity lambda

output_it = fn(x) -> IO.puts(x) end
Enum.each([1, 2, 3], output_it)

28 – Enum.into/2 transfers anything that is enumerable into anything that is considered collectable.

          |> Enum.into(HashDict.new)

Matching and Guard Clauses

29 – Think of “=” as an example of pattern matching. It is called the match operator. The right side is matched to the variable on the left side.

list = [1, 2]

30 – You can use the anonymous variable for matching.

{_, time} = :calendar.local_time

31 – You can do “chain matching”.

a = b = c = 2 + 2 - can chain matching

32 – You can use guard clauses

defmodule Example do
  def test_it(y) when y > 0 do
    :positive
  end

  def test_it(0), do: :zero

  def test_it(y) when y < 0 do
    :not_allowed_to_be_negative
  end
end

33 - You can have multiclause lambdas

example = fn
  y when is_number(y) and y < 0 ->
    :negative
  y when is_number(y) and y >= 0 ->
    :integer
end

Summary

This list of hacks should help you get going with Elixir basics, especially if you’re coming from a Ruby background.


Profile picture

Written by Bruce Park who lives and works in the USA building useful things. He is sometimes around on Twitter.