Elixir
Hal E. Fulton
Ruby Day 13 Nov 2015 Turin, Italy
for the
Rubyist
Why Ruby?
- Radically object-oriented
- Beautiful syntax
- Consistent semantics
- Highly expressive
- But... not very concurrent
- And some say it is slow
Why Erlang?
- Functional language
- Good support for concurrency
- BEAM
- Small, fast, lightweight processes
- OTP
"If somebody came to me and wanted to pay me a lot of money to build a large-scale message handling system that really had to be up all the time, could never afford to go down for years at a time, I would unhesitatingly choose Erlang to build it in."
Tim Bray
Sun Microsystems
Why not Erlang?
- Ugly syntax?
- Inconsistencies
- Old-fashioned pieces
- "Onions in the varnish"
Onions in the Varnish?
Primo Levi
In The Periodic Table, Primo Levi tells a story that happened when he was working in a varnish factory. He was a chemist, and he was fascinated by the fact that the varnish recipe included a raw onion. What could it be for? No one knew; it was just part of the recipe. So he investigated, and eventually discovered that they had started throwing the onion in years ago to test the temperature of the varnish: if it was hot enough, the onion would fry.
Paul Graham
Why Elixir?
- Runs on BEAM
- Compatible with Erlang
- Can access OTP
- Prettier syntax?
- Modernized language features
- Borrows from Ruby
Elixir...
- Obviously borrows from Erlang
- Also borrows from Ruby
- Also innovates (borrows from elsewhere)
Erlang
Ruby
Elixir
Anonymous functions
sum = &(&1 + &2)
c = sum.(4,3)
sum = fn(a, b) -> a + b end
c = sum.(4, 3)
The "pipeline" operator
means
filing = DB.find_customers
|> Orders.for_customers
|> sales_tax(2015)
|> prepare_filing
f(x,y)
filing = prepare_filing(sales_tax(Orders.for_customers(DB.find_customers),2015))
people = DB.find_customers
orders = Orders.for_customers(people)
tax = sales_tax(orders, 2015)
filing = prepare_filing(tax)
x |> f(y)
defmodule AnagramFinder do
def search_file(file) do
get_words(file)
|> process_all
|> Enum.each &print_words/1
end
defp process_all(list) do
list
|> Enum.group_by(HashDict.new, &(Enum.sort(String.codepoints(&1))))
end
defp get_words(file) do
File.read!(file)
|> String.split("\n", trim: true)
end
defp print_words({_key, [_word]}), do: nil
defp print_words({_key, words}) do
[first | rest] = words
|> Enum.reverse
IO.puts "#{first}: #{Enum.join(rest, ", ")}"
end
end
AnagramFinder.search_file("/usr/share/dict/words")
Anagram Finder in Elixir
Other features...
- Multiple function heads
- Guard clauses
- Macros
- Protocols
- Streams
- Tasks
- Small, fast, lightweight processes
- Process supervision
Multiple function heads
defmodule Fibonacci do
def fib(0), do: 1
def fib(1), do: 1
def fib(n), do: fib(n-2) + fib(n-1)
end
IO.puts Fibonacci.fib(5) # 8
IO.puts Fibonacci.fib(10) # 89
Guard clauses
# on a named function
def circle_area(radius) when is_number(radius)
3.1416 * radius * radius
end
# on an anonymous function
func = fn
x when is_number(x) -> "a number"
x when is_binary(x) -> "a binary"
x -> "something else"
end
Macros
defmodule MyMacros do
defmacro unless(test, expr) do
quote do
if(!unquote(test), do: unquote(expr))
end
end
end
# in another context:
require MyMacros
unless 2 == 3, do: IO.puts "That's a relief."
Two observations...
Multiple function heads, pattern matching, and guard clauses can all help reduce branching logic.
Tail recursion makes it possible in many cases to reduce looping.
Avoiding branches
defmodule Quadratic do
def real_roots(a, b, c) when b*b - 4*a*c > 0, do: 2
def real_roots(a, b, c) when b*b - 4*a*c == 0, do: 1
def real_roots(a, b, c) when b*b - 4*a*c < 0, do: 0
end
Avoiding loops
defmodule Multi do
def print(1, str), do: IO.puts str
def print(n, str) do
IO.puts str
print(n-1, str)
end
end
greeting = "Salve mundo!"
Multi.print(5, greeting)
Where does this
ultimately lead?
Functional programming...
Fast messaging...
Multiple processors/cores
Those who
dreamed
early...
What is "object-oriented" really?
"I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages. So messaging came at the very beginning -- it took a while to see how to do messaging in a programming language efficiently enough to be useful."
Alan Kay
"Can Programming Be Liberated
from the von Neumann Style?
A Functional Style
and Its Algebra of Programs"
Turing Award Lecture
of John Backus, 1978
The machine, as we envisioned it, would contain a million tiny computers, all connected by a communications network. We called it a "Connection Machine."
W. Daniel Hillis
"Richard Feynman and the Connection
Machine," Physics Today, 1989
But enough dreaming
and back to reality...
Note: I'm working on a new book.
I hope you will buy it someday.
Questions?
Anagram Finder in Elixir
defmodule AnagramFinder do
def search_file(file) do
get_words(file)
|> process_all
|> Enum.each &print_words/1
end
defp process_all(list) do
list
|> Enum.group_by(HashDict.new, &(Enum.sort(String.codepoints(&1))))
end
defp get_words(file) do
File.read!(file)
|> String.split("\n", trim: true)
end
defp print_words({_key, [_word]}), do: nil
defp print_words({_key, words}) do
[first | rest] = words
|> Enum.reverse
IO.puts "#{first}: #{Enum.join(rest, ", ")}"
end
end
AnagramFinder.search_file("/usr/share/dict/words")
1.0e3
grazie!
deck
By hal_9000
deck
- 240