Pro Yearly is on sale from $80 to $50! »

Foray into Functional Programming with Elixir

9fa60df551b9e4282c0519bf173c2e04?s=47 Trevor Brown
November 26, 2014

Foray into Functional Programming with Elixir

Functional programming languages provide many advantages over traditional OO languages. Functional languages make data manipulation (e.g. parsing binary data) easier. Their lack of mutable state (all data is immutable) also makes functional applications simpler than their OO counterparts. Learning how to solve problems and write software in functional languages like Elixir can be difficult at first. Writing real-world applications in functional languages requires a different approach to problem solving. In this talk I cover core concepts specific to functional programming required to start writing simple Elixir applications quickly. I will also share some things I have discovered over the past year that have helped me make the transition from OO to functional programming. Focusing on data and data manipulations is key to being productive in a functional language.

9fa60df551b9e4282c0519bf173c2e04?s=128

Trevor Brown

November 26, 2014
Tweet

Transcript

  1. Foray into Functional Programming with Elixir Trevor Brown

  2. Trevor Brown Sarasota, Fl Ruby, JavaScript, Erlang, and Elixir Erlang

    developer at Voalte
  3. Elixir? •  Built on top of the Erlang VM • 

    Compiles down to BEAM bytecode •  Helps to have an understanding of 
 Erlang and the underlying VM
  4. What is Erlang? •  Designed for scalability
 and real-time systems

    •  Functional •  Simplifies concurrent 
 programming •  Fault-Tolerant (“let it crash” error handling philosophy) •  Bytecode runs on the Erlang VM •  Designed by Joe Armstrong in 1986 •  Legendary nine 9’s of uptime
  5. What is Elixir?

  6. Elixir •  Dynamic •  Functional •  Built on top of

    the Erlang VM •  Leverages the VM to build concurrent, distributed and fault-tolerant applications. •  Created in 2011 by José Valim •  1.0.0 was released in September
  7. Myth “Elixir is like Ruby on the Erlang VM”
 • 

    Elixir is VERY different from Ruby, but the syntax is similar –  Functional vs OO –  Pattern Matching –  Recursion –  Immutable Data Structures
  8. Myth “Elixir is just CoffeeScript for Erlang” •  Elixir provides

    better tooling, first-class docs, and easier metaprogramming than Erlang •  Additional data types
  9. Why use Elixir? •  All the benefits of Erlang • 

    Easily reuse Erlang libraries •  No additional performance costs •  Better tooling, which allows for greater productivity •  Better syntax •  Simplified metaprogramming
  10. Elixir Basics

  11. Data Types •  All data is immutable •  Atoms are

    just like symbols in Ruby •  Booleans have been faked using the atoms ‘true’ and ‘false’ •  Strings :hello_world   :a   true  #  booleans  are  also  atoms   Atoms
  12. Binaries and Numbers #  Binaries   <<97,98,99>>   <<"abc">>  

        #  Integers   12       #  Floats   23.5  
  13. Tuples •  Ordered collection of elements with a fixed size.

    •  No array type, only linked lists. •  Each item in the list contains the value, along with a pointer to the next item in the list. #  Tuple   {:one,  :two,  :three}       #  Tuples  that  are  stored  contiguously  in  memory   {1,2,3,4}       #  We  can  access  a  tuple  element  with  the  `elem`   function   elem({1,  2,  3},  0)  #=>  1  
  14. Lists •  No array type, only linked lists. •  Each

    item in the list contains the value, along with a pointer to the next item in the list. #  List   [1,  2,  3]       letters  =  [:a,  :b,  :c]       #  Items  can  be  added  to  front  of  the  list  easily   [:d|letters]  #=>  [:d,  :a,  :b,  :c]  
  15. String(ish) Types •  No true string type •  Elixir represents

    strings as binaries underneath •  Lists can also be used to represent string data "José"  #=>  "José"  -­‐  UTF-­‐8  binary  string   'char  list'  #=>  'char  list'  -­‐  char  list       #  `?a`  returns  the  ASCII  integer  for  the  letter  `a`   ?a  #=>  97       <<?a,  ?b,  ?c>>  #=>  "abc"   [?a,  ?b,  ?c]      #=>  'abc'  
  16. #  Maps   %{  one:  1,  two:  2,  three:  3

     }       #  HashDicts   [one:  1,  two:  2,  three:  3]       #  Keyword  Lists  allow  duplicate  values   [one:  1,  one:  "One",  two:  2]       #  Structs   defmodule  User  do      defstruct  username:  "",  email:  "",  real_name:  ""   end   More Data Types… •  Maps, HashDicts, Keyword Lists, and Structs
  17. Modules •  Code organization is done via modules and functions.

    defmodule  Ping  do      @moduledoc  """      Sample  module      """          #  Add  functions  here   end    
  18. Functions •  In Elixir functions are identified by name and

    arity. #  add/2  has  two  clauses   def  add(0,  0)  do:  0     def  add(x,  y)  do:  x  +  y       #  add/3  has  one  clause     def  add(x,  y,  z)  do        x  +  y  +  z   end    
  19. Pattern Matching

  20. Pattern Matching •  Pattern Matching is used extensively in Elixir

    •  The equal signs isn’t assignment, it’s a challenge to make both sides equal :a  =  :a  #=>  :a   :a  =  :b  #=>  **  (MatchError)  no  match  of  right   hand  side  value:  :b     letter  =  :a  #=>  :a   letter  #=>  :a  
  21. Matching Tuples •  Tuples can be used in pattern matching

    {:ok,  foo}  =  {:ok,  "foo  bar  baz"}   foo  #=>  “foo  bar  baz”       {:ok,  foo}  =  {:error,  :crashed}  #=>  **   (MatchError)  no  match  of  right  hand  side  value:   {:error,  :crashed}    
  22. Matching Lists •  Lists can be used in pattern matching

    [head|tail]  =  [1,2,3]   head  #=>  1   tail  #=>  [2,3]     [first,second,third]  =  [1,2,3]   second  #=>  2     [one,two]  =  [1,2,3]  #=>  **  (MatchError)  no  match  of   right  hand  side  value:  [1,  2,  3]     [first,second|rest]  =  [1,2,3]   second  #=>  2  
  23. Variables in Patterns •  Variables cannot be re-bound to new

    values during a match •  If a variable name appears twice in a pattern the values it is matched against must be equal {num,  num}  =  {1,  1}  #=>  {1,  1}   {num,  num}  =  {1,  2}  #=>  **  (MatchError)  no  match   of  right  hand  side  value:  {1,  2}    
  24. Underscore Variable •  Can be used in place of a

    normal variable in a pattern •  No values are ever bound to it, meaning it can match any value anytime it is used. •  It's common to assign a value to `_` if we don't need it {_,  _}  =  {1,  1}  #=>  {1,  1}   {_,  _}  =  {1,  2}  #=>  {1,  2}   {num,  num}  =  {1,  2}  #=>  **  (MatchError)  no  match   of  right  hand  side  value:  {1,  2}  
  25. The ^ Variable Prefix •  Variables prefixed with the caret

    force patterns to match against the value previously bound to the variable username  =  "José"       {:name,  ^username}  =  {:name,  "Joe"}   #=>  **  (MatchError)  no  match  of  right  hand  side   value:  {:name,  "Joe"}       {:name,  ^username}  =  {:name,  "José"}   #=>  {:name,  "José"}  
  26. Pattern Matching •  Functions parameters are patterns #  These  are

     not  parameters,  they  are  patterns   #  Evaluated  if  value  passed  in  matches  `0`   def  fib(0)  do  0  end     #  Evaluated  if  value  passed  in  matches  `1`   def  fib(1)  do  1  end     #  Evaluated  if  the  first  two  clauses  don't  match   def  fib(n)  do  fib(n-­‐1)  +  fib(n-­‐2)  end    
  27. Pattern Matching •  Case statements also use pattern matching case

     do_something(work)  do      {:ok,  result}  -­‐>  #  success          do_something_with_result(result)      {:invalid_work,  error}  -­‐>  #  special  case          handle_invalid_work(error)      error  -­‐>  #  error          handle_error(error)   end  
  28. First Class Functions

  29. First Class Functions •  All functions in Elixir are first

    class •  There are two types of functions: –  Named – does not inherit scope –  Anonymous – inherits the scope of wherever it was defined functions •  Both can be referenced and passed around
  30. Named Functions defmodule  Hello  do      def  greet("SunJUG")  do

     "hello  everyone!"  end      def  greet(name)  do  "hello  "  <>  name  end   end       hello  =  &Hello.greet/1  #=>   #Function<6.90072148/1  in  :erl_eval.expr/5>     hello.("SunJUG")  #=>  "hello  everyone!”     Hello.greet("José")  #=>  "hello  José  
  31. Anonymous Functions special_greeting  =  "hello  everyone!”     #  Inherits

     scope  creating  closure   greet  =  fn      "SunJUG"  -­‐>  special_greeting        name  -­‐>  "hello  "  <>  name   end       greet  #=>  #Function<6.90072148/1   in  :erl_eval.expr/5>     greet.("SunJUG")  
  32. Passing Functions user1  =  [first_name:  "Joe",  last_name:  "Armstrong"]   user2

     =  [first_name:  "José",  last_name:  "Valim"]   users  =  [user1,  user2]       #  Function  that  gets  the  :first_name  from  a  keyword   list   first_name  =  fn(user)  -­‐>      Keyword.fetch!(user,  :first_name)   end       #  Map  the  first_name  function  to  every  item  in  the   users  list   Enum.map(users,  first_name)  #=>  ["Joe",  "José"]  
  33. Passing Functions user1  =  [first_name:  "Joe",  last_name:  "Armstrong"…]   user2

     =  [first_name:  "José",  last_name:  "Valim"…]   users  =  [user1,  user2]       #  Function  that  returns  a  function  that  fetches  the   `field`  key  value  from  a  keyword  list   get_field  =  fn(field)  -­‐>      fn(user)  -­‐>          Keyword.fetch!(user,  field)      end   end       Enum.map(users,  get_field.(:last_name))  #=>   ["Armstrong",  "Valim"]  
  34. Recursion

  35. •  Recursive functions call themselves •  Recursion functions are used

    when repetition is needed •  If a recursive function always calls itself and never returns an infinite loop will be created Recursion
  36. •  Here we recursively calculate the sum of a list

    •  Note the empty list clause which ends recursion Recursion Step by Step User  invokes   sum_list([1,2,3,4],  0)   sum_list([2,3,4],  0+1)   sum_list([3,4],  2+1)   sum_list([4],  3+3)   sum_list([],  4+6)   10   def  sum_list([head  |  tail],  acc)  do      sum_list(tail,  acc  +  head)   end       def  sum_list([],  acc)  do      acc   End  
  37. Another Example def  factorial(0)  do      1   end

          def  factorial(number)  do      factorial(number  -­‐  1)  *  number   end   •  A recursive function that computes factorials
  38. Another Example def  factorial(0)  do      1   end

          def  factorial(number)  do      factoral(number  -­‐  1)  *  number   end   •  A recursive function that computes factorials User  invokes  factorial(5)   factorial(5-­‐1)  *  5   factorial(4-­‐1)  *  4  *  5   factorial(3-­‐1)  *  3  *  4  *  5   factorial(2-­‐1)  2  *  3  *  4  *  5   factorial(1-­‐1)  *  1  *  2  *  3  *  4  *  5   1  *  1  *  2  *  3  *  4  *  5   60  
  39. Tail Call Optimization •  If the last expression is solely

    another function call the function is tail call optimized •  This means: –  the no new stack level is created –  function is simply re-executed with the new arguments •  Not all functions that call themselves on the last line are tail call optimized •  If a function is not tail call optimized it will run without errors until the stack uses all the memory
  40. Tail Call Optimization def  factorial(0,  result)  do      result

      end       def  factorial(number,  result)  do      factorial(number  -­‐  1,  result  *  number)   end   def  factorial(0)  do      1   end       def  factorial(number)  do      factorial(number  -­‐  1)  *  number   end   Optimized Not Optimized
  41. Concurrency

  42. The Erlang VM •  Each instance of the Erlang VM

    is called a node •  You can have one or more nodes on a physical machine •  You can spread nodes across multiple physical machines •  Nodes are one OS process, with one or more threads
  43. The Erlang VM CPU   CPU   CPU   CPU

      Thread   Thread   Thread   Thread   Virtual  Machine  Process   Process   Process   Process   Process   Process   Process  
  44. Processes •  Erlang processes are not OS processes •  Erlang

    VM distributes work of Erlang processes between the OS threads of the VM’s OS process •  Processes are concurrent •  There is no “main” process. Everything is a process and there are not special types of processes
  45. Processes •  Processes are independent of each other •  Processes

    cannot modify each other •  No data is shared between processes •  Processes communicate via message passing •  Processes are extremely lightweight •  Processes can be linked together, processes can also be monitored by other processes
  46. Message Passing

  47. Processes are Actors •  Implements the actor model •  Processes

    encapsulate state •  Elixir’s processes are more object-oriented than objects in object-oriented languages
  48. Processes •  Message being sent to a process •  A

    process receiving messages #  `pid`  is  a  process   message  =  {:add,  1,  2}   send  pid,  message   #  Listen  for  messages   receive  do      {:add,  x,  y}  -­‐>          x  +  y      {:subtract,  x,  y}  -­‐>          x  -­‐  y      _  -­‐>          :error   end  
  49. PingPong •  Two processes sending messages back and forth • 

    Resulted in an infinite loop <0.83.0>   Ping <0.97.0>   Pong {:ping,  <0.83.0>}   {:pong,  <0.97.0>}   <0.59.0>   Shell Process (Us) {:ping,  <0.83.0>}   spawn_link   Receive  blocks  
  50. ProcessChain •  Thousands of processes arranged in a loop passing

    hundreds of messages around the loop. •  Demonstrates the efficiency of the Erlang VM’s message passing and lightweight nature of processes
  51. ProcessChain <0.87.0>   Link <0.97.0>   Chain Controller msg  

    <0.59.0>   Shell Process (Us) {:start,  <0.83.0>}   spawn_link   <0.83.0>   Link msg   msg   :shutdown   :shutdown  
  52. ProcessChain <0.97.0>   <0.38.0>   Chain Link Controller <0.83.0>  

    Chain Link <0.84.0>   Chain Link <0.77.0>   Chain Link <0.43.0>   Chain Link <0.26.0>   Shell (Us)
  53. Trying to DDOS an Elixir App

  54. Things I didn’t cover •  OTP - Open Telecom Platform

    •  Communication between nodes on different machines •  Metaprogramming •  Protocols •  The amazing pipe operator •  List comprehensions •  Much more!
  55. Go Try Out Elixir •  Install Erlang •  Install Elixir

    •  Instructions for installation are at http://elixir-lang.org/getting_started/1.html
  56. Trevor Brown @Stratus3D github.com/Stratus3D stratus3d.com voalte.com Code: 
 https://github.com/Stratus3D/ foray_into_functional_programming_with_elixir

    Slides: https://speakerdeck.com/stratus3d
  57. Elixir vs. other languages Language   Pure   Typing  

    Immutable   Data   Elixir/Erlang   No   Dynamic   Yes   Haskell   Yes   Sta`c   Yes   Clojure   No   Dynamic   Yes   Scala   Yes   Dynamic   Par`al?   JavaScript   No   Dynamic   Par`al?   Python   No   Dynamic   Par`al?  
  58. Process Relationships •  Process spawn other processes
 •  Processes can

    be linked •  Processes can also monitor other processes #  Spawn  a  process   pid  =  spawn(worker_fun)   #  Link  the  process  calling   `link`  function  with  `pid`   process   link  pid   #  Monitor  `pid`  process  from   the  process  calling  the   `monitor`  function   monitor  pid  
  59. Elixir vs. Erlang •  Syntax Example defmodule  Math  do  

       def  square(x)  do          x  *  x      end   end       Enum.map  [1,2,3],   &Math.square/1   #=>  [1,  4,  9]   -­‐module(math).   -­‐export([square/1]).       square(X)  -­‐>  X  *  X.       lists:map(fun  math:square/1,   [1,  2,  3]).   %=>  [1,  4,  9]   Elixir   Erlang