Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Elixir

Introduction to Elixir

Basic Introduction to Elixir

Jean-Marcel Belmont

May 06, 2015
Tweet

More Decks by Jean-Marcel Belmont

Other Decks in Programming

Transcript

  1. What is Elixir? Elixir is a dynamic, functional language designed

    for building scalable and maintainable applications. Elixir leverages the Erlang VM, known for running low- latency, distributed and fault-tolerant systems, while also being successfully used in web development and the embedded software domain. — http://www.elixir-lang.org
  2. Misconceptions about Elixir Elixir is CoffeeScript for Erlang Ruby on

    Rails Type Development for Erlang Erlang beefed with nicer syntax and some tools
  3. Why Elixir? Chip designers are under so much pressure to

    deliver ever-faster CPUs that they’ll risk changing the meaning of your program, and possibly break it, in order to make it run faster http://www.gotw.ca/publications/concurrency-ddj.htm The Free Lunch Is Over A Fundamental Turn Toward Concurrency in Software By Herb Sutter
  4. Companies using Erlang • Amazon • Yahoo • Facebook •

    T-Mobile • Motorola • VMWare • Heroku • Ericsson • WhatsApp • CouchDB • Github • Basho • Call of Duty
  5. Solution? ⇒ Functional Programming Pure Functions are stateless. Pure Functions

    always produces the same output for a particular input. Programming Order of Execution doesn't matter. Common pitfalls of concurrent environments such as deadlocks, race conditions, etc. related to order of execution can be avoided much more easily. Stateless functions are easier to reason about. Result ⇒ DeadLocks, Mutexes, Semaphores ⇒ Gone
  6. Benefits of Pure Functions Laziness: Only Evaluate when output is

    needed Memoization ⇒ is a specific form of caching that involves caching the return value of a function based on its parameters No Order Dependencies ⇒ Evaluate statements in any order Easily Parallelizable [Immutability, No Side Effects, Memoizable, Laziness] ⇒ Concurrency Problem Goes Away
  7. Aspects of Functional Programming Immutable Data Structures Functional Composition ⇒

    In computer science, function composition (not to be confused with object composition) is an act or mechanism to combine simple functions to build more complicated ones. Function Currying ⇒ the process of transforming a function that takes multiple arguments into a function that takes just a single argument and returns another function if any arguments are still needed. Pattern Matching
  8. Here Comes Elixir Sample Elixir Programs: defmodule Factorial do def

    of(0), do: 1 def of(n) when n > 0 do n * of(n-1) end end defmodule Fibonacci do def fibonnaci(n) do when n == 0, do: 0 when n == 1, do: 1 when n > 1 do fibonnaci(n - 2) + fibonnaci(n - 1) end end end
  9. Comments and documentation # Comments in elixir begin like this

    @moduledoc """ Multiline Syntax documentation for modules in Elixir. Once you compile an Elixir Program you can run “h ModuleName” """ @doc """ Multiline syntax documentation for functions and run h ModuleName.somefunction """
  10. Basic Types in Elixir Integer ⇒ 1234 0xcafe 0177 0b100

    0o77 10_000 # x marks hexadecimal, b marks binary, o mark octal, underscore is separator Float ⇒ 1.0 3.1415 6.02e23 # must be prefixed with digit so cannot have .3 Atom ⇒ :foo :me@home :"with spaces" # Strings and Char Lists "hello" # string 'hello' # char list # Multi-line strings ⇒ # "I'm a multi-line\nstring.\n" """ I'm also a multi-line string. """
  11. Math Operators # Math Operators 1 + 1 #=> 2

    10 - 5 #=> 5 5 * 2 #=> 10 10 / 2 #=> 5.0 #=> In elixir the operator `/` always returns a float. # To do integer division use `div` div(14, 2) #=> 7 # To get the division remainder use `rem` rem(10, 3) #=> 1
  12. Comparison Operators # For comparisons we have: `==`, `!=`, `===`,

    `!==`, `<=`, `>=`, `<` and `>` 1 == 1 #=> true 1 != 1 #=> false 1 < 2 #=> true # `===` and `!==` are more strict when comparing integers and floats: 1 == 1.0 #=> true 1 === 1.0 #=> false # We can also compare two different data types: 1 < :hello #=> true
  13. Boolean Operators # There are also boolean operators: `or`, `and`

    and `not`. # These operators expect a boolean as their first argument. true and true #=> true false or true #=> true # 1 and true #=> ** (ArgumentError) argument error # If you are an Erlang developer, and and or in Elixir actually map to the andalso and orelse operators in Erlang. # Elixir also provides `||`, `&&` and `!` which accept arguments of any type. # All values except `false` and `nil` will evaluate to true. 1 || true #=> 1 false && 1 #=> false nil && 20 #=> nil !true #=> false
  14. Data Structures, Binaries, Strings Tuple ⇒ { 1, 2, :ok,

    "xy" } # Array Like Data Structure List ⇒ [ 1, 2, 3 ] # Similar to Linked List [head | tail] Keyword List ⇒ (can have duplicate keys) ⇒ [ a: "Foo", b: 123 ] Map ⇒ (cannot have duplicate keys) ⇒ %{ key => value, key => value } Binary ⇒ << 1, 2 >> or "abc" # Strings are really just binaries, while Char lists are just Lists. String Interpolation ⇒ "I am an #{interpolated_string}" Truth Operators ⇒ true, false, nil Range Operator ⇒ a..b
  15. Miscellaneous Operators # String concatenation binary1 <> binary2 #=> “Hello”

    <> “World” ⇒ “HelloWorld” # List Concatenation list1 ++ list2 #=> [1, 2] ++ [3, 4] ⇒ [1, 2, 3, 4] # Set Difference list1 -- list2 #=> [1, 2, 3] -- [2] ⇒ [1, 3] # Membership operator a in enum #=> 1 in [1, 2, 3] ⇒ true Restrict Re-assignment operator ^term #=> x = 1, 1 = x, but then ^x = 2 ⇒ ** (MatchError) no match of right hand side value: 2
  16. Control Flow # `if` expression if false do "This will

    never be seen" else "This will" end # There's also `unless` unless true do "This will never be seen" else "This will" end # `cond` lets us check for many conditions at the same time. # Use `cond` instead of nesting many `if` expressions. # cond uses pattern matching cond do 2 + 4 == 3 -> "I will never be seen" 2 * 5 == 12 -> "Me neither" 1 + 2 == 3 -> "But I will" end
  17. Pattern Matching # Many control-flow structures in Elixir rely on

    Pattern Matching. # `case` allows us to compare a value against many patterns: case {:one, :two} do {:four, :five} -> "This won't match" {:one, x} -> "This will match and bind `x` to `:two`" _ -> "This will match any value" end
  18. Error Handling Operators # `try/catch` is used to catch values

    that are thrown, it also supports an # `after` clause that is invoked whether or not a value is caught. try do throw(:hello) catch message -> "Got #{message}." after IO.puts("I'm the after clause.") end # In Elixir, we avoid using try/rescue because we don't use errors for control flow. We take errors literally: they are reserved to unexpected and/or exceptional situations. # Let it fail is the Elixir/Erlang model
  19. Sigils in Elixir Sigils provide Language facility for developers to

    extend the languages features The most common sigil in Elixir is ~r, which is used to create regular expressions regex = ~r/foo|bar/ # A regular expression that matches strings which contain "foo" or "bar "foo" =~ regex #=> true “buzz” =~ regex #=> false 8 different delimiters you can use with sigils ~r/hello/ ~r|hello| ~r"hello" ~r'hello' ~r(hello) ~r[hello] ~r{hello} ~r<hello> Sigils also support heredocs, that is, triple double- or single-quotes as separators The ~s sigil is used to generate strings ⇒ ~s(this is a string with "double" quotes, not 'single' ones) ⇒ "this is a string with \"double\" quotes, not 'single' ones" The ~c sigil used to generate char lists ⇒ ~c(this is a char list containing 'single quotes') 'this is a char list containing \'single quotes\'' The ~w sigil is used to generate lists of words that are delimited by white space ~w(foo bar bat) ⇒ ["foo", "bar", "bat"] Custom Sigils can be made with sigil_{identifier} pattern
  20. Anonymous Functions in Elixir # Anonymous functions (notice the dot)

    square = fn(x) -> x * x end square.(5) #=> 25 # Anonymous functions with clauses and guards. # Guards let you fine tune pattern matching, # they are indicated by the `when` keyword: f = fn x, y when x > 0 -> x + y x, y -> x * y end #=> iex: f.(1, 3) #=> 4 #=> iex: f.(-1, 3) #=> -3
  21. Builtin Functions in Elixir # Elixir also provides many built-in

    functions. # These are available in the current scope. is_number(10) #=> true is_list("hello") #=> false elem({1,2,3}, 0) #=> 1 div(2, 2) #=> 1 rem(4, 3) #=> 1 # Many others in http://elixir-lang.org/docs/stable/elixir/
  22. Named Function in Elixir # You can group several functions

    into a module. # Inside a module use `def` to define your functions. defmodule MathStuff do def sum(a, b) do a + b end def square(x) do x * x end def power_of_3(z), do: z * z * z end
  23. Shortcut Function Syntax # Also called Capture Syntax square =

    fn(x) -> x * x end square = &(&1 * &1) #=> square.(3) ⇒ 9 string_concat = &(&1 <> &2) #=> string_concat.(“Hello”, “World”) ⇒ “HelloWorld” fun = &List.flatten(&1, &2) # You can also capture Custom Modules fun.([1, [[2], 3]], [4, 5]) &List.flatten(&1, &2) is the same as writing fn(list, tail) -> List.flatten(list, tail) end
  24. Custom Module Definition # You can group several functions into

    a module. # Inside a module use `def` to define functions. defmodule Math do defmodule Math do def sum(a, b) do def sum(a,b), do: &(&1 + &2) a + b end def square(x), do: &(&1 * &1) end def square(x) do x * x end end Math.sum(3, 5) #=> 8
  25. Recursion in Elixir # Recursion is used often in Elixir

    defmodule RecursionFunctions do @doc “”” Function sums up all the elements of an empty list “”” def sum_list([head | tail], acc) do sum_list(tail, acc + head) end def sum_list([], acc) do acc end end # Recursion.sum_list([5,4,3], 0) #=> 12
  26. Attributes in Elixir # Elixir modules support attributes, there are

    built-in attributes # You can also define your own attributes defmodule AttributeExample do @moduledoc """ This is a built-in attribute on an attribute example module. """ @eighty_nine 89 # This is a custom attribute. def add_attribute(x) do @eighty_nine + x end IO.inspect(@eighty_nine) #=> 89 end
  27. Structs in Elixir # Structs are extensions on top of

    maps that bring default values, # Structs are just bare maps underneath # Structs bring compile-time guarantees and polymorphism into Elixir. defmodule Person do defstruct name: nil, age: 0, height: 0 # only can define one struct at a time end some_dude = %Person{ name: "Marcel", age: 34, height: 240 } some_dude.name #=> Marcel some_gal = %{ some_dude | name: "Catherine", age: 22, height: 119 }
  28. Macros in Elixir # Implement unless using if defmodule MacroExample

    do defmacro unless(clause, options) do quote do: if(!unquote(clause), unquote(options)) end end # macros must be required before invoked require MacroExample MacroExample.unless false do IO.puts “Here I am now” end
  29. Protocols in Elixir • Protocols are a mechanism to achieve

    polymorphism in Elixir defprotocol Blank do @doc "Returns true if data is considered blank/empty" def blank?(data) end # Integers are never blank defimpl Blank, for: Integer do def blank?(_), do: false end # Just empty list is blank defimpl Blank, for: List do def blank?([]), do: true def blank?(_), do: false end
  30. Enumerables in Elixir # Enumurable example map_list_of_nums = Enum.map(1..3, fn

    x -> x * 2 end) #=> [2, 4, 6] map_list_of_nums = Enum.map(1..3, &(&1 * 2)) #=> [2, 4, 6] odd? = &(rem(&1, 2) != 0) # define a function that tests whether number is odd # Ugly form of function chaining Enum.sum(Enum.filter(Enum.map(1..100_000, &(&1 * 3)), odd?)) # The Elixir way 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum • All the functions in the Enum module are eager. Many functions expect an enumerable and return a list back
  31. Comprehensions in Elixir • Comprehensions in Elixir allow you to

    quickly build data structures from • an enumerable or a bitstring • iex> for n <- [1, 2, 3, 4, 5], do: n * 3 #=> [3, 6, 9, 12, 15] • The `<-` is called a generator • Comprehension can take many generators and filters Example comprehension with 3 generators for x <- [1, 2, 3], y <- [4, 5, 6], z <- [7, 8, 9], rem(n, 2) == 0, do: x * y * z #=> [28, 32, 36, 35, 40, 45, 42, 48, 54, 56, 64, 72, 70, 80, 90, 84, 96, 108, 84, 96, 108, 105, 120, 135, 126, 144, 162] Example comprehension with a filter for n <- [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n #=> [2, 4, 6] You can combine both generators and a filter in a comprehension
  32. Streams in Elixir • Streams are lazy, composable enumerables •

    Instead of generating intermediate lists, streams create a series of computations that are invoked only when we pass it to the Enum module • Streams are useful when working with large, possibly infinite, collections. # Stream Example odd? = &(rem(&1, 2) != 0) stream_list_of_nums = 1..100_000_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum
  33. Concurrency in Elixir • Elixir relies on the Actor model

    for concurrency • All we need to write concurrent programs in elixir are three primitives: ◦ spawning processes, sending messages and receiving messages. • Actor is a Process • A process performs a specific task when it receives a message • After receiving a message it can reply to the sender • All messages go to a process Queue ◦ Messages are in a Queue for unprocessed messages sent from other ◦ processes that have not been consumed • Processes are isolated from each other, run concurrent to one another and communicate via message passing ◦ Share-nothing Asynchronous Message Passing • Distributed systems sometimes called "shared nothing" systems because the message passing abstraction hides underlying state changes that may be used in the implementation of sending messages ### Wikipedia ###
  34. Concurrency in Action # To start a new process we

    use the `spawn` function, which takes a function # as argument. f = fn -> 2 * 2 end spawn(f) #=> #PID<0.40.0> spawn fn -> 2 * 2 end pid = spawn fn -> 1 + 2 end Process.alive?(pid) #=> false # Process.alive?(self()) send self(), {:hello, "world"} receive do {:hello, msg} -> msg {:world, msg} -> "won't match" end #=> “world”
  35. Sample Send/Receive Elixir Module # ~spawn~ returns a pid (process

    identifier), you can use this pid to send messages to the process. # To do message passing we use the ~send~ operator. # Then we need to receive messages that are sent. This is achieved with the `receive` mechanism: defmodule SampleSendReceive do def area_calculation do receive do {:rectangle, w, h} -> IO.puts("Area of rectangle is #{w * h}") area_calculation() {:square, s} -> IO.puts("Area of square is #{s * s}") area_calculation() end end end
  36. Sample run of Send/Receive Module # compile program in iex

    ⇒ c “SampleSendReceive.ex” pid = spawn(fn -> SampleSendReceive.area_calculation() end) # Rectangle pattern matched send pid, {:rectangle, 2, 3} or pid |> send({:rectangle, 2, 3}) #=> Area of rectangle is 6 #=> {:rectangle, 2, 3} # Square pattern matched pid |> send({:square, 3}) #=> Area of square is 9 #=> {:square, 3}
  37. Mix Build Tool and Built-in Unit Tests > mix new

    mt --module Meetup * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/mt.ex * creating test * creating test/test_helper.exs * creating test/mt_test.exs Your mix project was created successfully. You can use mix to compile it, test it, and more: cd mt; mix test Run `mix help` for more commands.
  38. What’s Next More in-depth overview of OTP (Open-Telecom Platform) in

    Elixir/Erlang Web Framework Library ⇒ Phoenix ORM like Library ⇒ Ecto Package Manager ⇒ Hex Unit Testing in Elixir Lightning Talks
  39. Thank You for Attending Please post any questions you may

    have on the meetup discussion board.