Elixir for Erlang developers

Igor Kapkov, Evil Martians

Elixir for Erlang developers

Igas

Igor Kapkov

Evil Martians

@igasgeek / [email protected]

History

Why we ♥ Erlang

Why we'll ♥ Elixir

Design Goals

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

Differences

Operator Names

Erlang Elixir Meaning
and NOT AVAILABLE Logical 'and', evaluates both arguments
andalso and Logical 'and', short-circuits
or NOT AVAILABLE Logical 'or', evaluates both arguments
orelse or Logical 'or', short-circuits
=:= === A match operator
=/= !== A negative match
/= != Not equals
=< <= Less than or equals

Types

Variables

Pin Operator / Static Single Assignment

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

Default arguments

      defmodule Concat do
        def join(a, b, sep \\ " ") do
          a <> sep <> b
        end
      end
       
      IO.puts Concat.join("Hello", "world")      #=> Hello world
      IO.puts Concat.join("Hello", "world", "_") #=> Hello_world
    

UTF8

      iex> string = "hełło"
      "hełło"
      iex> byte_size string
      7
      iex> String.length string
      5
      iex> ?a
      97
      iex> ?Å‚
      322
      iex> String.codepoints("hełło")
      ["h", "e", "Å‚", "Å‚", "o"]
    

Function capturing

      iex> Enum.all? [:a, :b, :c], &is_atom(&1)
      true
       
      iex> Enum.map  ["a", "B", "c"], &String.upcase &1
      ["A", "B", "C"]
       
      iex> Enum.sort [1, 2, 3], &( &2 < &1 )
      [3, 2, 1]
       
      iex> fun = &(&1 + &2 + &3)
      ...> fun.(1, 2, 3)
      6
    

Sigils

      ~w(--source test/enum_test.exs) #=> ["--source", "test/enum_test.exs"]
    
      defmodule My do
        defmacro sigil_i(string, []) do
          quote do: for(p <- String.split(unquote(string)), do: String.to_atom(p))
        end
      end
    
      ~i(asdf asdf) #=> [:asdf, :asdf]
    

Structs

      iex> defmodule User do
      ...>   defstruct name: "jose", age: 27
      ...> end
      {:module, User,
       <<70, 79, 82, ...>>, {:__struct__, 0}}
      iex> %User{}
      %User{name: "jose", age: 27}
      iex> %User{name: "eric"}
      %User{name: "eric", age: 27}
    

Exceptions

      File.read("hello.txt")
      #=> {:ok, "World"}
       
      File.read("invalid.txt")
      #=> {:error, :enoent}
       
      File.read!("hello.txt")
      #=> "World"
       
      File.read!("invalid.txt")
      #=> ** (File.Error) could not read file invalid.txt: no such file or directory
    

Pipe operator

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

Namespaces

      defmodule Foo do
        defmodule Bar do
        end
      end
       
      defmodule Foo.Bar do
      end
    

Comprehensions

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

Streams

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

Alias, Import, Require

DRY

      for {codepoint, _upper, lower, _title} <- codes, lower && lower != codepoint do
        defp do_downcase(unquote(codepoint) <> rest) do
          unquote(:binary.bin_to_list(lower)) ++ downcase(rest)
        end
      end
    

DSL

        defmodule User do
          use Ecto.Model
         
          schema "user" do
            field :name
            field :age, :integer
          end
        end
      
        get "/pages/:page", Controllers.Pages, :show, as: :page
      

Everything is an expression

        iex> 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, ...>>,</70,>
         {:hello_world, 0}}
      

Metaprogramming

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

Alan Kay

Macros

      defmacro left ^^^ right do
        quote do: :erlang.bxor(unquote(left), unquote(right))
      end
    

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
        end
      

ExUnit

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

Docstrings

Doctests

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

IEx / REPL

Also

Interoperability: Erlang ↝ Elixir

        iex> :crypto.rand_uniform(0, 10)
        3
         
        iex> :math.pi
        3.141592653589793
         
        iex> :ets.new(:cats, [:set, :named_table])
        :cats
      

Interoperability: Elixir ↝ Erlang

      1> 'Elixir.String':downcase(<<"ASD">>).
      <<"asd">>
       
      2> Task = 'Elixir.Task':async(fun() -> 1 + 1 end).
      3> 'Elixir.Task':await(Task).
      2
    

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