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