Elixir

Igor Kapkov, Evil Martians

Elixir

Igas

Igor Kapkov

Evil Martians

@igasgeek / [email protected]

Erlang

History

Design Goals

Elixir

Syntax

Syntax is user interface.

Brendan Eich

Programs must be written for people to read, and only incidentally for machines to execute.

Harold Abelson and Gerald Jay Sussman

Types

Pattern Matching

When I went to school, my math teacher said, “If there’s an X in several different parts in the same equation, then all the Xs mean the same thing.” That’s how we can solve equations: if we know that X+Y=10 and X-Y=2, then X will be 6 and Y will be 4 in both equations.

Joe Armstrong

Pattern Matching

        iex(1)> list = [1, 2, 3]
        [1, 2, 3]
      
        iex(2)> [a, 1, b] = list
        ** (MatchError) no match of right hand side value: [1, 2, 3]
      
        iex(3)> a..b = 1..10
        1..10
      
        iex(4)> a
        1
      
        iex(5)> b
        10
      
        iex(6)> [1, _, _] = [1, 2, 3]
        [1, 2, 3]
      

Static Single Assignment

        iex(1)> [a, a] = [1, 1]
        [1, 1]
      
        iex(2)> a
        1
      
        iex(3)> [a, a] = [1, 2]
        ** (MatchError) no match of right hand side value: [1, 2]
      
        iex(4)> ^a = 2
        ** (MatchError) no match of right hand side value: 2
      
        iex(5)> [^a, 2, 3] = [1, 2, 3]
        [1, 2, 3]
      
        iex(6)> [1, ^a] = [1, 2]
        ** (MatchError) no match of right hand side value: [1, 2]
      

Immutability

GOTO was evil because we asked, "how did I get to this point of execution?" Mutability leaves us with, "how did I get to this state?"

Jessica Kerr

Immutability

        iex(1)> list1 = [3, 2, 1]
        [3, 2, 1]
      
        iex(2)> list2 = [4 | list1]
        [4, 3, 2, 1]
      
        iex(3)> list = [1, 3, 2]
        [1, 3, 2]
      
        iex(4)> Enum.sort list
        [1, 2, 3]
      
        iex(5)> list
        [1, 3, 2]
      

List Comprehensions

        iex(1)> for x <- [1, 2, 3, 4, 5], do: x * x
        [1, 4, 9, 16, 25]
      
        iex(2)> for x <- [1, 2, 3, 4, 5], x < 4, do: x * x
        [1, 4, 9]
      
        iex(3)> for x <- [1, 2], y <- [5, 6], do: x * y
        [5, 6, 10, 12]
      
        iex(4)> for x <- [1, 2], y <- [5, 6], do: {x, y}
        [{1, 5}, {1, 6}, {2, 5}, {2, 6}]
      

Recursion

        iex(1)> [head | tail] = [1, 2, 3]
        [1, 2, 3]
      
        iex(2)> head
        1
      
        iex(3)> tail
        [2, 3]
      
        iex(4)> [1 | [2 | [3|[]]]]
        [1, 2, 3]
      
         
        defmodule MyList do
          def len([]), do: 0
          def len([head|tail]), do: 1 + len(tail)
        end
      

Everything is an expression

        iex(1)> defmodule First do
        ...>   IO.puts "Compiling"
        ...>   def hello_world do
        ...>     IO.puts "Hello, world!"
        ...>   end
        ...> end
      
        Compiling
      
        {:module, First,
         <<70, 79, 82, 49, 0, 0, 7, 124, 66, 69, 65, 77, 65, 116, 111, ...>>,
         {:hello_world, 0}}
      

Anonymous Functions

        iex(1)> answer = fn
        ...>   42 -> IO.puts "Right!"
        ...>   _  -> IO.puts "Wrong!"
        ...> end
      
        #Function<6.80484245 in :erl_eval.expr/5>
      
        iex(2)> answer.(42)
        Right!
        :ok
      
        iex(3)> answer.('password')
        Wrong!
        :ok
      

Higher Order Functions

        iex(1)> list = [1, 3, 5, 7, 9]
        [1, 3, 5, 7, 9]
      
        iex(2)> Enum.map list, fn elem -> elem * 2 end
        [2, 6, 10, 14, 18]
      
        iex(3)> Enum.map list, fn elem -> elem * elem end
        [1, 9, 25, 49, 81]
      
        iex(4)> Enum.map list, fn elem -> elem > 6 end
        [false, false, false, true, true]
      
        iex(5)> square = &(&1 * &1)
        #Function<6.80484245 in :erl_eval.expr/5>
      
        iex(6)> Enum.map list, square
        [1, 9, 25, 49, 81]
      

|>

        filing = prepare_filing(sales_tax(Orders.for_customers(DB.find_customers), 2013))
      
        filing = DB.find_customers
                 |> Orders.for_customers
                 |> sales_tax(2013)
                 |> prepare_filing
      

Guards

        defmodule Guard do
          def what_is(x) when is_number(x) do
            IO.puts "#{x} is a number"
          end
          def what_is(x) when is_list(x) do
            IO.puts "#{inspect(x)} is a list"
          end
          def what_is(x) when is_atom(x) do
            IO.puts "#{x} is an atom"
          end
        end 
        Guard.what_is(99)        # => 99 is a number
        Guard.what_is(:cat)      # => cat is an atom
        Guard.what_is([1,2,3])   # => [1,2,3] is a list
      

Protocols

        defprotocol Inspect do
          def inspect(thing, opts)
        end
         
        defimpl Inspect, for: Atom do
          def inspect(false),  do: "false"
          def inspect(true),   do: "true"
          def inspect(nil),    do: "nil"
        end
         
        defimpl Inspect, for: Integer do
          def inspect(thing, _opts), do: integer_to_binary(thing)
        end
      

Lisp isn't a language, it's a building material.

Alan Kay

Metaprogramming

Macros

        defmodule My do
          defmacro left ** right do
            :math.pow(left, right)
          end
        end
      
         
        defmodule Test do
          import My
          IO.puts 2 ** 3
        end
      
         
        $ elixir macros.exs
        8.0
      

Streams

        iex(1)> Enum.map(1..10_000_000, &(&1*&1)) |> Enum.take(5)
        [1, 4, 9, 16, 25]
      
        iex(2)> Stream.map(1..10_000_000, &(&1*&1)) |> Enum.take(5)
        [1, 4, 9, 16, 25]
      
        iex(3)> Stream.iterate(2, &(&1*2)) |> Enum.take(10)
        [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
      
        iex(4)> Stream.unfold({0,1}, fn {f1,f2} -> {f1, {f2, f1+f2}} end)
        ...(4)> |> Enum.take(15)
        [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
      

Implement map

        defmodule MyList do
          def map([], _func), do: []
          def map([ head | tail ], func), do: [ func.(head) | map(tail, func) ]
        end
      

Actors

        defmodule Parallel do
          def pmap(collection, fun) do
      
            me = self
      
            collection
      
          |>
            Enum.map(fn (elem) ->
      
              spawn_link fn -> (send(me, { self, fun.(elem) })) end
      
            end)
          |> 
            Enum.map(fn (pid) -> receive do { ^pid, result } -> result end end)
          end
        end
      

ExUnit

        defmodule MathTest do
          use ExUnit.Case
         
          test "basic operations" do
            assert 1 + 1 == 2
          end
        end
      

Doctests

        @doc """
        Checks if the given argument is nil or not.
        Allowed in guard clauses.
         
        ## Examples
         
            iex> nil?(1)
            false
            iex> nil?(nil)
            true
         
        """
      

EVM Projects

Books

Talks

Verdict

I think it’s pretty cool, I think we’re going to have fun together.

Joe Armstrong

Elixir

Igor Kapkov, Evil Martians