On Github tracehelms / talk_elementary_elixir
Talk by Trace Helms
Elixir is
2 million websocket connections!
On a single server!
You don't assign variables, you pattern match.
Maybe variables get assigned as a side effect.
iex> x = 2 2 iex> y = 3 3 iex> 2 = x 2 # waaaat? iex> 2 = y ** (MatchError) no match of right hand side value: 3
iex> x = 2 2 iex> x = 3 3 iex> x 3 iex> ^x = 2 # caret forces no re-binding of variables ** (MatchError) no match of right hand side value: 2
Erlang holds onto the original x behind the scenes.
Side note: it's spelled caret.(your variables don't get changed)
iex> list = ["this", "is", "my", "list"] ["this", "is", "my", "list"] iex> Enum.sort(list) ["is", "list", "my", "this"] iex> list ["this", "is", "my", "list"] iex> list = Enum.sort(list) ["is", "list", "my", "this"]
You have to reassign list if you want it to stick.
Probably familiar from some Ruby code.
iex> {:ok, result} = {:ok, 42} {:ok, 42} iex> result 42 iex> {:ok, result} = {:error, 42} ** (MatchError) no match of right hand side value: {:error, 42} iex> {_, result} = {:whatever, 42} {:whatever, 42}
defmodule Fibonacci do def fib(0), do: 1 def fib(1), do: 1 def fib(n), do: fib(n - 2) + fib(n - 1) end
Note: no if statements.
This code is runnable in the /code directory.Cleans up your code
# normal way process(parse_args(args)) # elixir way args |> parse_args |> process
def process({user, project, count}) do Issues.GithubIssues.fetch(user, project) |> decode_response |> convert_to_list_of_hashdicts |> sort_into_ascending_order |> Enum.take(count) |> print_table_for_columns(["number", "created_at", "title"]) end
Uh oh...
iex> [head | tail] = [1, 2, 3, 4] [1, 2, 3, 4] iex> head 1 iex> tail [2, 3, 4]
iex> [head | tail] = [1] [1] iex> head 1 iex> tail []
defmodule MyList do def square([]), do: [] def square([head | tail]) do [head * head | square(tail) ] end endThis code is runnable in the /code directory.
Let's prove that they're lightweight.
Let's fire off a million processes.
defmodule Processes do def counter(next_pid) do receive do n -> send(next_pid, n+1) end end def create_processes(n) do last = Enum.reduce(1..n, self, fn(_, send_to) -> spawn(Processes, :counter, [send_to]) end) send last, 0 receive do final_answer when is_integer(final_answer) -> "Result is #{inspect(final_answer)}" end end def run(n) do IO.puts inspect :timer.tc(Processes, :create_processes, [n]) end endThis code is runnable in the /code directory.
$ iex processes.exs iex> Processes.run(200_000) {1456993, "Result is 200000"} :ok iex> Processes.run(300_000) ** (SystemLimitError) a system limit has been reached 13:07:26.075 [error] Too many processesFirst element of tuple is elapsed time in microseconds. So 1.45 seconds.
Let's go for 1,000,000! Tell Erlang to prepare itself...
$ elixir --erl "+P 1000000" -r processes.exs -e "Processes.run(1_000_000)"
A million processes...in under 8 seconds...on my MacBook.
{7857020, "Result is 1000000"}
Let's see them with observer!
$ iex iex> :observer.start
I'm going to calculate a Fibonacci number...
On one of your machines...
From my machine.
$ ifconfig # get ip address from en0 ... $ iex --name node_name@your_ip_address --cookie cookie_name iex> c("dist_fib.exs") [Fibonacci]Fire up Activity Monitor to see your CPU working.
iex> Node.connect(:"nodetwo@10.0.1.24") true iex> Fibonacci.spawn_fib(:"nodetwo@10.0.1.24", 5) The result is 8 :ok iex> Fibonacci.spawn_fib(:"nodetwo@10.0.1.24", 45) ...
This code is available in the repo to review.
$ brew install elixir
Most of these examples came from the book Programming Elixir by Dave Thomas (the Prag Prog guy).