Skip to content

Instantly share code, notes, and snippets.

@adamrubin
Last active January 8, 2016 20:22
Show Gist options
  • Save adamrubin/a6ad495ec9e3a04fc5c1 to your computer and use it in GitHub Desktop.
Save adamrubin/a6ad495ec9e3a04fc5c1 to your computer and use it in GitHub Desktop.

#Learning Elixir

#Basics * Functional * Immutable * Dynamically typed * JIT compilation * In Erlang, variable names can't be re-bound. In Elixir they can. * Elixir has polymorphism, Erlang does not. * Erlang has macros for commonly used functions which require recurrsion under the hood. * Elixir has no return (the last thing evaluated is returned) * Uses the Beam VM (same as Erlang) #Types ##Numbers

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment