You Should Try Elixir – Nathan Feaver, Senior Developer @ AgileStyle – Example: FizzBuzz



You Should Try Elixir – Nathan Feaver, Senior Developer @ AgileStyle – Example: FizzBuzz

1 2


talk-try-elixir

Why You Should Try Elixir. A talk by Nathan Feaver

On Github jfeaver / talk-try-elixir

You Should Try Elixir

Nathan Feaver, Senior Developer @ AgileStyle

Example: FizzBuzz

fizzbuzz(n) = { FizzBuzz if rem( n/3 ) = 0 and rem( n/5 ) = 0 Fizz     if rem( n/3 ) = 0 Buzz     if rem( n/5 ) = 0 n

Example: FizzBuzz

defmodule FizzBuzz do
  def fizz_word(0, 0, _), do: "FizzBuzz"
  def fizz_word(0, _, _), do: "Fizz"
  def fizz_word(_, 0, _), do: "Buzz"
  def fizz_word(_, _, n), do: n

  def fizzbuzz(n) do
    fizz_word( rem(n,3), rem(n,5), n )
  end

  def result(n) do
    Enum.map(1..n, fn(n) ->
      fizzbuzz(n)
    end)
  end

  def print(n) do
    result(n)
      |> Enum.join(", ")
      |> IO.puts
  end
end

FizzBuzz.print(100)
Pattern Matching | Pipe Operator Functional Bits: pattern matching, pipe operator, recursion features
Elixir has changed the way I think about programming. Not just in terms of "Oh, its a functional language", it's actually changed my conception of what it means to program.~ Dave Thomas

Why Should I Try Elixir?

Other People Are Doing It

Elixir has changed the way I think about programming. Not just in terms of "Oh, its a functional language", it's actually changed my conception of what it means to program.~ Dave Thomas Taken from: Think Different
I want to long term use [Elixir and the Phoenix Framework] to replace all of my client work and everything I want to build personally~ Chris McCord Taken from: Rise of The Phoenix - Building an Elixir Web Framework
Programming in Erlang sucks. Elixir is everything good about Erlang and none — almost none — of the bad.~ Devin Torres Taken from: The Excitement of Elixir Devin Torres is a well-known contributor in the erlang open source community.

José Valim

Why Should I Try Elixir?

It's Built on the Erlang Virtual Machine

Erlang

Erlang was originally developed in 1986 by Ericsson for telephony applications to support highly concurrent systems

  • Mature Language/Tools/Community
  • Concurrency (OTP)
  • Distributed (OTP)
  • Fault Tolerant
  • Hot code swapping

Erlang libraries are simple to use!

Elixir doesn't have built-in random or time libraries (although there are multiple packages). Let's use Erlang's libraries:

defmodule Random do
  def uniform do
    :random.uniform
  end

  def seed do
    :random.seed(:erlang.now)
  end
end

Why Should I Try Elixir?

It's Easy to Get Started

Great Toolset for a Young Language

  • brew install elixir
  • Rake ~> Mix
  • Bundler ~> Hex
  • IRb ~> IEx
  • RSpec ~> ExUnit

Why Should I Try Elixir?

Curiosity About Functional Languages

Other option I would advocate is Closure

Curiosity About Concurrency

Concurrency

Metaprogramming

defmacro match?(left, right) do
  quote do
    case unquote(right) do
      unquote(left) ->
        true
      _ ->
        false
    end
  end
end
iex> list = [{:a,1},{:b,2},{:a,3}]
[a: 1, b: 2, a: 3]
iex> Enum.filter list, fn (thing) -> match?({:a, _}, thing) end
[a: 1, a: 3]
Taken from: Elixir: It's Not About Syntax Built from the beginning with a focus on metaprogramming using macros

The Phoenix Web Framework

Jobs!

But not in Edmonton

Okay, I Want to Try Elixir!

THE END

nathan@agilestyle.com

Slides on GitHub: jfeaver/talk-try-elixir

Some More Elixir

Head | Tail Recursion

defmodule Math do
  def sum_list([head|tail], accumulator) do
    sum_list(tail, head + accumulator)
  end

  def sum_list([], accumulator) do
    accumulator
  end
end

Math.sum_list([1, 2, 3], 0) #=> 6
Taken from: Elixir Getting Started: Recursion

Immutability

Concurrency/Distribution (OTP)

# Compile the module and create a process that evaluates `area_loop` in the shell
pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0>

# Send a message to `pid` that will match a pattern in the receive statement
send pid, {:rectangle, 2, 3}
#=> Area = 6
#   {:rectangle,2,3}

send pid, {:circle, 2}
#=> Area = 12.56000000000000049738
#   {:circle,2}

# The shell is also a process, you can use `self` to get the current pid
self() #=> #PID<0.27.0>
Taken from: Learn X in Y Minutes - 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.

Pattern Matching

iex> a = [ 1,2,3 ]
iex> b = [[ 1,2,3 ]]
iex> [c] = [[ 1,2,3 ]]
iex> a == c
true
iex> [ x,y,z ] = [ 1,2,3 ]
iex> z
3
iex> "Today is " <> date = "Today is December 16th"
iex> date
"December 16th"
defmodule MyApp do
  def callback(:ok, data) do
    # success
  end

  def callback(:error, data) do
    # handle failure
  end
end
Back To FizzBuzz pervasive You're not assigning 1 to a, you're challenging elixir to find a way to make that true

Pipe Operator

The pipe operator(|>) is most helpful when transforming data through a sequence. The operator pipes the result of an expression into the first argument of the next function. Confusing, nested function calls no longer need temporary variables to make them readable:

# Confusing, nested function calls
def print(n) do
  IO.puts( Enum.join( result(n), ", " ) )
end

# versus using the pipe operator
def print(n) do
  result(n)
    |> Enum.join(", ")
    |> IO.puts
end
Back To FizzBuzz

Coming Back to Ruby

Can I apply this in my day job?

Updating Records Functionally?

it 'releases one invite credit when it is withdrawn' do
  sender = @invite.sender
  expect{ @invite.withdraw; sender.reload }.
    to change{ sender.available_invite_credits.length }.
    by(1)
end
it 'releases one invite credit when it is withdrawn' do
  sender = @invite.sender
  expect{ @invite.withdraw }.
    to change{ User.available_invite_credits(sender.id).length }.
    by(1)
end

Another Attempt

# find the author counts
author_counts = []
author_counts_index = -1
current_author = ''
for i in 0...commits.length
  commit = commits[i]
  if commit.author == current_author
    author_counts[author_counts_index][1] += 1
  else
    current_author = commit.author
    author_counts << [commit.author, 1]
    author_counts_index += 1
  end
end
commits.sort_by do |commit|
  commit.author
end.
  chunk do |commit|
    commit.author
  end.
    each do |author, author_commits|
      puts "#{author} has #{author_commits.count} commits"
    end

Ruby is an OO Language, Default to OO Idioms

  • Use functional style blocks
  • Bottom-up versus Top-down
  • Tell Don't Ask
  • Read POODR by Sandi Metz

My approaches to some Ruby problems have probably changed in subtle ways. Learning Elixir has been fun and will help me learn other languages (and their strengths) faster. I'm probably also better prepared to use Proc objects, the Proc#curry method, and Enumerator::Lazy objects but directly practicing them would be even better (perhaps with katas).