Starting and changing mindset with Elixir

What is elixir?

Created in 2012 Elixir is functional and dynamic language, which make a real option if we want to create a web application. Since, Elixir runs under Erlang virtual machine (BEAM), which make it really stable and mature.
Created by José Valim, who is a Ruby on Rails enthusiastic and had helped the community a lot. So, he took the the best of Rails and merge it with the efficiency of Elixir + Erlang.

Elixir is special for high availability systems .

Functional

The first two meanings we have to forget are objects and classes. It’s all about functions that operate and transform data, pure functions. But, We can not rely on Elixir being a pure functional language.

Dynamic

Elixir variables don’t need to be declared as an specifics data type, It checks at runtime.

Inmutable

Data structure won’t change. For example:

# Uses the match operator to give a true on function
name = “Han Solo”

# It creates a total new value for the new variable
name = "Luke Skywalker"

Concurrency

This is something that Elixir can sell without any help, It’s a big feature here. Since, Elixir runs under BEAM, concurrency is much easier. Code running simultaneously, Thanks Erlang !

Reliability

Elixir is a young language, but It runs on Erlang VM, which is pretty old and a reliable system, one of he most around the globe.

Easy to read and write

By taking some Ruby stuff, Elixir decided to go with the syntax, which is good for us. The code is easy to read and o write.

Modules or classes ?

As I mentioned before classes are not considered in Elixir, every function is stored and handled in a modules, as their namespace.

defmodule HelloModule do
  def say_hi do
    IO.puts "Hello World !"
  end
end

Structs

This is a map where we set up a set of keys and their default values. It’s defined in the module.

defmodule User do
  defstruct name: "John", roles: []
end

iex> %User{name: "Han", roles: [:pilot, :shooter]}
User{name: "Steve", roles: [:pilot, :shooter]}

Strings

A string a basically a sequence of bytes, UTF-8 encoded binary. Elixir provides to us a set of functions to interact with our strings.

Lenght.

length/1
It returns the number of bytes in our string.
iex> String.length "Walter White"
12

Replace

replace/3
It returns a new string, It receives three parameters, the string to be changed, the pattern to be replaced and the replacement string.
iex> String.replace("Seed","e","i")"Siid"

Duplicate

duplicate/2
It returns a specific string duplicated the number of times seted
iex> String.duplicat("Hello",3)"Hello Hello Hello "

Split

split/2
Ir returns a list based on the patter of split.
iex> String.split("Hello, I'm Bond, James Bond", ",")["Hello", " I'm Bond", " James Bond"]

Collections

List

A simple collection of values, where there can be any type of data.

iex> ["String", 12.3 , :atom]["String", 12.3, :atom]

List Concatenation

++/2

iex> ["Luke", "Leia"] ++ ["Han"]
["Luke", "Leia", "Han"]

List Subtraction

--/2

iex> ["String", 12.3 , :atom] -- [:atom]
["String", 12.3]

Head / Tail

Heads is the first element of our list and the tail is te remaining elements on the list.

iex> hd ["Head", 222, :tail]
"Head"

iex> tl ["Head", 222, :tail]
[222, :tail]

Keyword Lists

It’s an associative list composed by two tuples, where the key have to be an atom data type, they are ordered and keys can be given more than once.

iex> [foo: "bar", hello: "world"]
[foo: "bar", hello: "world"]

iex> list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]

iex> list ++ [{:c, 3}]
[a: 1, b: 2, c: 3]

Maps

Maps are more flexible compare to keyword lists, keys can be any value, included variables and maps keys doesn’t follow any kind of ordering.

iex> key = :one

iex> list = %{key => 1, :two => 2, "three" => 3, 4 => 4}
%{:one => 1, :two => 2, "three" => 3, 4 => 4}

# Get an specific value from a given key.
iex> Map.get(list, key)
1

# Add a tuple to the current list.
iex> Map.put(list, :five, 5)
%{:one => 1, :two => 2, "three" => 3, 4 => 4, :five => 5}

# Return the map on list format. 
iex> Map.to_list(list)
[{4, 4}, {:atom, 1}, {:five, 5}, {:two, 2}, {"three", 3}]

Enum

It is a set of algorithms to be used over collections. In this section we will just show some of them. You can check all of them over here.

All

all?/2
We supply a fn(x) where will be run for all items on our collection. Will return true if all invocations returns true, with just one that returns false the entire method will return false.

iex> Enum.all?([1,2,3], fn(number) -> number < 5 end )
true

iex> Enum.all?([1,2,3], fn(number) -> number < 2 end )
false

Any

any?/2
We supply a fn(x) where will be run for all items on our collection. Will return true if at list one invocation returns true, otherwise will return false.

iex> Enum.any?([1,2,3], fn(number) -> number < 2 end )
true

iex> Enum.any?([1,2,3], fn(number) -> number == 5 end )
false

Chunk By

chunk_by/2
Specially if we need to group our collections based in a given function.

iex> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)
[["one", "two"], ["three"], ["four", "five"]]

Each

each/2
Invokes the given function for each item on the collection. It returns an atom :ok

iex> Enum.each(["one", "two", "three"], fn(x) -> IO.puts(x) end)
one
two
three
:ok

Map

map/2
Invokes the given function for each item on the collection. It returns a new collection with new values.

iex> Enum.map(["one", "two"], fn(x) -> String.upcase(x) end)
["ONE", "TWO"]

Member

member?/2
Checks if an item exists in a collection.

iex>  Enum.member?(["one", "two", "three"], "three")
true

Reject

reject/2
Return a new collection of items that returns false from the given fn(x).

iex> Enum.reject([1,2,3,4,5,6], fn(x) -> Integer.is_even(x) end)
[1, 3, 5]

Sort

sort/2
It sorts the collection by the given fn(x).

iex> Enum.sort([%{:val => 2}, %{:val => 3}, %{:val => 1}], fn(x, y) -> x[:val] > y[:val] end)
[%{val: 3}, %{val: 2}, %{val: 1}]

Unique By

unique_by/2
Remove all duplicated into our collection

iex> Enum.uniq([1, 2, 3, 2, 1]) 
[1, 2, 3]

Pipe Operator

The pipe operator |> passes the result of an expression as the first parameter of another expression.
When I was using elixir by the first time this operator called my attention immediately. Since functional programming is about sending data and transforming it thought functions it can get really messy, but the pipe operator is something to help us on that job.

Our problem:

# Return the final value of a product
formated_price(taxes(commision(product_value(), 0.01), 0.13))

Some OO solution:
prod_val = product_value()
prod_commision = commision(prod_val, 0.01)
prod_taxes = taxes(prod_commision, 0.13)
prod_final_value = formated_price(prod_taxes)
prod_final_value

Our Elixir Solution:

product_value()
|> commision(0.01)
|> taxes(0.13)
|> formated_price()
As you can see every return value of a function is passed as the first parameter of the following function. It makes our code really easy to read.

Pattern Matching. Isn’t it just assignment?

This is a deep functionality in Elixir. To understand this a little bit more I have to say that = operator doesn’t necessarily means “assign something to a variable”, instead it really means “match the left hand side to the right hand side”. It turns the whole expression into a equation.

iex> x = 1
1

iex> 1 = x
1

iex>  2 = x
** (MatchError) no match of right hand side value: 1

Useful approaches

Pattern matching becomes really useful when we use it with tuples, functions or recursion.

# We want to assign to match value with te number 2
iex> {:ok, value} = {:ok, 2}
{:ok, 2}

iex> value
2
# If the atom value :ok doesn't match It will return a fail

iex> {:ok, value} = {:none, 2}
** (MatchError) no match of right hand side value: {:none, 2}

It seems really cool, but check this, something more functional

defmodule Greet do
  def say_hi(tuple) do
    case tuple do
      {:morning, name} ->
        "Morning #{name} !"
      {:evening, name} ->
        "Good evening #{name} !"
      _ ->
        "Default Hi !!"
    end
  end
end

iex> Greet.say_hi({:morning, "Luke"})
"Morning Luke !"

iex> Greet.say_hi({:morning, "Han"})
"Morning Han !"

iex> Greet.say_hi({:defaut, "Nobody"})
"Default Hi !!"

Finally, the more useful approach to me is by using Pattern Matching on functions definitions:

defmodule Greet do
  def say_hi(:morning, name) do
    "Morning #{name} !"
  end
  def say_hi(:evening, name) do
    "Good Evening #{name} !"
  end
  def say_hi(name) do
    "Default Hi #{name} !"
  end
end

iex> Greet.say_hi(:morning, "Luke")
"Morning Luke !"

iex> Greet.say_hi(:morning, "Han")
"Morning Han !"

iex> Greet.say_hi("Leila")
"Default Hi Leila !"

Conclusion

To me, having a Ruby on Rails background, Elixir seems pretty nice and I’m looking forward go deeper and deeper into this language. This entrance is part of an introduction We did in 4geeks, you can check the full video:


Popular posts from this blog

Introducing 4Geeks Payments

How We Ensure the Best Devs to Work On Your Project

How Important Is Take Care of the Company Culture