Slide 1

Slide 1 text

Foray into Functional Programming with Elixir Trevor Brown

Slide 2

Slide 2 text

Trevor Brown Sarasota, Fl Ruby, JavaScript, Erlang, and Elixir Erlang developer at Voalte

Slide 3

Slide 3 text

Elixir? •  Built on top of the Erlang VM •  Compiles down to BEAM bytecode •  Helps to have an understanding of 
 Erlang and the underlying VM

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

What is Elixir?

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Myth “Elixir is just CoffeeScript for Erlang” •  Elixir provides better tooling, first-class docs, and easier metaprogramming than Erlang •  Additional data types

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

Elixir Basics

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Binaries and Numbers #  Binaries   <<97,98,99>>   <<"abc">>       #  Integers   12       #  Floats   23.5  

Slide 13

Slide 13 text

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  

Slide 14

Slide 14 text

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]  

Slide 15

Slide 15 text

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       <>  #=>  "abc"   [?a,  ?b,  ?c]      #=>  'abc'  

Slide 16

Slide 16 text

#  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

Slide 17

Slide 17 text

Modules •  Code organization is done via modules and functions. defmodule  Ping  do      @moduledoc  """      Sample  module      """          #  Add  functions  here   end    

Slide 18

Slide 18 text

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    

Slide 19

Slide 19 text

Pattern Matching

Slide 20

Slide 20 text

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  

Slide 21

Slide 21 text

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}    

Slide 22

Slide 22 text

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  

Slide 23

Slide 23 text

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}    

Slide 24

Slide 24 text

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}  

Slide 25

Slide 25 text

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é"}  

Slide 26

Slide 26 text

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    

Slide 27

Slide 27 text

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  

Slide 28

Slide 28 text

First Class Functions

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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é  

Slide 31

Slide 31 text

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")  

Slide 32

Slide 32 text

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é"]  

Slide 33

Slide 33 text

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"]  

Slide 34

Slide 34 text

Recursion

Slide 35

Slide 35 text

•  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

Slide 36

Slide 36 text

•  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  

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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  

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Concurrency

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

The Erlang VM CPU   CPU   CPU   CPU   Thread   Thread   Thread   Thread   Virtual  Machine  Process   Process   Process   Process   Process   Process   Process  

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Message Passing

Slide 47

Slide 47 text

Processes are Actors •  Implements the actor model •  Processes encapsulate state •  Elixir’s processes are more object-oriented than objects in object-oriented languages

Slide 48

Slide 48 text

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  

Slide 49

Slide 49 text

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  

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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  

Slide 52

Slide 52 text

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)

Slide 53

Slide 53 text

Trying to DDOS an Elixir App

Slide 54

Slide 54 text

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!

Slide 55

Slide 55 text

Go Try Out Elixir •  Install Erlang •  Install Elixir •  Instructions for installation are at http://elixir-lang.org/getting_started/1.html

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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?  

Slide 58

Slide 58 text

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  

Slide 59

Slide 59 text

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