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.
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.