#Learning Elixir
- Basics
- Types
- Numbers
- Atoms
- Strings
- Maps
- Tuples
- Lists
- Regex
- Boolean
- Modules
- Functions
- Structs
- Pattern Matching
- Operators
- Math
- Comparison
- List
- Binary
- Pipeline
- Control Flow
- Condition
- Case
- if/else/unless
- Guards
- Macros
- Documentation
- Processes
- IEX
- Supervisor
- Mesh
- Node
- OTP
- Mix
- Useful links
- Sample code
Integer
400
4_000_000
Float
12.34
0.12
## Atoms
* Like Ruby symbols
* Module names are actually atoms (e.g., MyModule == :MyModule)
* true, false, nil are atoms
:ok
:"separated by space"
true == :true
false == :false
nil == :nil
is_atom(:hello)
## Strings
- Double quotes (single is a char list)
- They're binaries under the hood
"Hello"
<<42>> ----> *
"""
this is a
multiline string
"""
"Hello" <> " " <> "World" ----> "Hello World"
## Maps
* Like a ruby hash
test = %{key: "value", foo: "bar"}
test.foo
test[:foo]
is_map(test)
# Replace key with "value2"
%{ test | key: "value2" }
## Tuples
* Fixed length collection of stuff
me = {"Adam", 40, "Married"}
elem(me,0)
elem(me,2)
put_elem(me,1,41)
## Lists
* Like a ruby array.
* Linked list
* variable length, prepending is quick, appending is slow
test = ["a","b","c",0,1,2]
is_list(test)
hd test ---> "a"
tl test ---> ["b","c",0,1,2]
Enum.at(test,0) ---> "a"
test = [0,1,2]
Enum.map test, fn(i) -> i*5 end
shorthand: Enum.map test, &(&1*5)
Enum.filter list, fn(el) -> is_number(el) end
shorthand: Enum.filter list, &is_number(&1)
- Each element is a tuple with 2 elements: an atom key and a value
my_kw_list = [{:key, "value"}, {:foo, "baz"}]
shorthand: my_kw_list = [key: "value", foo: "baz"]
my_kw_list[:key]
my_kw_list.key
Keyword.keys my_kw_list
Keyword.values my_kw_list
## Regex
~r/hello/
## Boolean
true
false
# Modules
* No classes, only modules
defmodule Math do
def foo(a,b) do
a+b
end
def hi, do: "hi"
end
Math.foo(1,2)
Math.hi
Nested
defmodule Math do
defmodule Addition do
end
end
Math.Addition.()
#Functions
##Anonymous
Basic example
add = fn(a,b) ->
a + b
end
add.(1,2)
Pattern matching a function's param:
print_name = fn
:adam -> "Hello, Adam"
:bob -> "Hello, Bob"
_ -> "Hello, stranger"
end
print_name.(:adam)
print_name.(:hank)
Passing a function into a function:
greet_person = fn
greeter, {:person, first_name, last_name} ->
greeter.("#{first_name} #{last_name}")
end
polite_greeter = fn
name -> "Hello, #{name}!"
end
angry_greeter = fn
name -> "What, #{name}?!"
end
greet_person(polite_greeter, {:person, "Adam", "Rubin"})
##Named##
- Have to be placed inside a module.
** Basic example **
defmodule Math do
def add(a,b) do
a+b
end
end
Math.add(1,2)
##Raise##
raise "error!"
##Structs
* Maps that have a struct attribute
* Compile-time checks
* Default values
defmodule User do
defstruct name: nil, email: nil
def first_name(user) do
user.name
|> String.split
|> List.first
end
end
end
user = %User{name: "Adam Rubin"}
User.first_name(user)
#Pattern Matching
```
=
4 = 5 won't return false, it'll return a match error
adam = "test" since it's a variable name, the match operator will make them equal
{name, age} = {"adam", 40} name and age are both bound at this point.
{age, age} = {40, 41} this would fail because variables can only be bound once per match
underscore can ignore a value {name, _} = {"adam", 40}
you can name your underscore for readability {name, _age} = {"adam", 40}
check response {:ok, file} = File.read("foo.txt")
name = "Adam" {name, 40} = {"Adam", 40} would assign "Adam" to name
name = "Bob" {^name, 40} = {"Adam", 40} will not assign because of the carrot, and would match instead, which would fail because "Bob" != "Adam".
a = 1 b = 1 a = b this would rebind a to b (1)
^a = b this would pattern match instead of rebinding
a = 2 ^a = b this would pattern match and fail
<a name="operators"/>
# Operators
<a name="math-operators"/>
## Math Operators
- / - * rem
rem is a modulus
<a name="comparison-operators"/>
## Comparison operators
1 == 1.0 ---> true 1 === 1.0 ---> false
<a name="list-operators"/>
## List operators
```"Adam" in ["Adam", "Rachael", "Nate", "Tim"]```
**combine/append**
[0,1,2] ++ [4,5]
**remove members**
[0,1,2] -- [4,5,0] ---> [1,2] [0,1,2] -- [1,4,5,0] ---> [2]
**prepend**
list = [0, 1, 2] [99 | list] ---> [99, 0, 1, 2]
**head/tail**
[a|b] = [1, 2, 3] a ---> 1 b ---> [2,3]
<a name="binary-operators"/>
## Binary operators
"Hello" <> " " <> "World" ---> "Hello World"
name = "Adam" "Hello, #{name}"
|| returns the first truthy expression
"Adam" || nil ---> "Adam" nil || "Adam" ---> "Adam" nil || false ---> nil name = user.name || "Adam"
<a name="pipeline-operator"/>
## Pipeline operator
var = var |> foo |> bar
"hello" |> IO.puts [1,2,3] |> Enum.map(fn i -> i*2 end)
<a name="control-flow"/>
# Control Flow
<a name="condition"/>
## Condition
cond do 1 + 1 == 1 -> "This is not true" 2 * 2 != 4 -> "This isn't true either" true -> "This is true!" end end
<a name="case"/>
## Case
case Stripe.Customer.create(attrs) do {:ok, customer} -> "A customer was created!" {:error, reason} -> "Womp womp" other -> "An unknown error occurred" end
calculate = fn expression -> case expression do {:+,num1,num2} -> num1 + num2 {:-,num1,num2} -> num1 - num2 {:/,num1,num2} -> num1 / num2 _ -> raise "uh oh #{inspect(expression)}" end end
calculate.({:+, 4, 5})
<a name="if"/>
## if/else/unless
* Elsif doesn't exist (use recursion)
* Unless exists, but not unless/else (!)
if 1 + 1 == 2 do "Good math!" else "This won't happen" end
if true, do: "TRUE", else: "FALSE"
unless 1+1 == 3 do end
<a name="guards"/>
## Guards
"when"
calculate = fn expression -> case expression do {:/,num1,num2} when num2 != 0 -> num1 / num2 _ -> raise "uh oh #{inspect(expression)}" end end
def credit(balance, amt) when amt > 0 do end
<a name="macros"/>
# Macros
Metaprogramming
<a name="documentation"/>
# Documentation
* @moduledoc
* @doc
* @spec
<a name="processes"/>
# Processes
* Elixir's unit of currency (like an object)
* millions can be spawned on a single machine
* actor model
* spawn
* receive
pid = Counter.start(0) Process.register pid, :foo send :foo, :inc
pid = spawn_link fn -> receive do IO.puts "boom" end end send(pid, :boom)
receive do message -> message after 3000 -> IO.puts "bye!" end
<a name="iex"/>
# IEX
**Compile**
```c "foo.exs" ```
**Load .ex file**
```iex my_module.ex ```
**Get previous value:**
``` v(-1) ```
**Get value of iex(13):**
``` v(13) ```
<a name="supervisor"/>
# Supervisor
<a name="mesh"/>
# Mesh
<a name="node"/>
# Node
<a name="otp"/>
# OTP (Open Telecom Platform)
<a name="mix"/>
# Mix
* start a new project
* build dependencies
* testing
<a name="useful-links"/>
#Useful links
* https://www.youtube.com/watch?v=5kYmOyJjGDM
* http://confreaks.tv/events/elixirconf2014
* https://www.learnelixir.tv
* https://pragprog.com/book/elixir/programming-elixir
* https://pragprog.com/book/phoenix/programming-phoenix
* http://learningelixir.joekain.com/
* http://phoenixscreencasts.com/
* http://elixirschool.com/
* http://elixirsips.com/
* http://www.theerlangelist.com/2014/06/understanding-elixir-macros-part-1.html
* https://soundcloud.com/elixirfountain
* http://codetunes.com/2015/phoenix-blog/
* https://github.com/h4cc/awesome-elixir
* https://elixir-slackin.herokuapp.com/
* http://pipe.cypresslab.net/function-composition-in-elixir/
<a name="sample-code"/>
#Sample code
defmodule Counter.Server do def start(initial_count \ 0) do spawn fn -> listen(initial_count) end end
defp listen(count) do receive do :inc -> listen(count + 1) :dec -> listen(count - 1) {sender, :val} -> send sender, count listen(count) end end end
defmodule Counter.Client do def inc(pid), do: send(pid, :inc) def dec(pid), do: send(pid, :dec) def val(pid) do send pid, {self, :val} receive do val -> val end end end
defmodule Rocket do def start_launch_sequence(start_at \ 10) when start_at >= 0 and start_at < 100 do IO.puts "Liftoff in..." countdown(start_at) end
defp countdown(0), do: blastoff defp countdown(seconds) do IO.puts seconds countdown(seconds - 1) end
defp blastoff do IO.puts "Blastoff!" end end
defmodule Counter do def start(initial_count \ 0) do spawn fn -> listen(initial_count) end end
def listen(count) do receive do :inc -> listen(count + 1) :dec -> listen(count - 1) {sender, :val} -> send sender, count listen(count) end end end