Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Elixir Cheetsheet

elixir.livemd

Elixir Cheetsheet

Pattern Matching

user = %{name: "tom", age: 23}
%{name: username} = user
username

Pattern Matching

defmodule Example do
  def greet(%{name: username}) do
    "hello " <> username
  end
end

user = %{name: "tom"}
Example.greet(user)

Control Flow

IF

if false do
  IO.puts("false")
else
  IO.puts("true in else")
end

Case

case {1, 2, 3} do
  {4, 5, 6} ->
    "wont match"

  {1, x, 3} ->
    "this will match our case,x = #{x}"

  _ ->
    "default match"
end

Cond

cond do
  1 + 1 == 3 ->
    "will not run"

  2 * 5 == 11 ->
    "wont run either"

  true ->
    "default condition fired"
end

With

with {:ok, {_num, _asdf}} <- Integer.parse("123asdf") do
  IO.puts("hey bub int")
else
  err ->
    err
end
{a, w} = Integer.parse("34somestring")
IO.puts("a: #{a}, w=#{w}")

Types

IO.puts(?a)
IO.puts(~c"hello")
is_atom(:na)

You can do type checks using following funcitons

  • is_atom/1

  • is_bin­ary/1

  • is_nil/1

  • is_bit­str­ing/1

  • is_list/1

  • is_num­ber/1

  • is_boo­lean/1

  • is_map/1

  • is_pid/1

  • is_fun­ction/1

  • is_tuple/1

  • is_port/1

  • is_fun­ction/2

  • is_ref­ere­nce/1

  • is_int­eger/1

  • is_float/1

Importing

require Redux # compiles a module

import Redux # compiles, and you can use without the Redux. prefix

use Redux # compiles, and runs Redux.­u­sin­g/1

use Redux, async: true

import Redux, only: [dupli­cate: 2]

import Redux, only: :functions

import Redux, only: :macros

import Foo.{Bar, Baz}

Numbers

Integer

import Integer

n = 12

n |> digits() # → [1, 2]

n |> to_charlist() # → ‘12’

n |> to_string() # → “12”

n |> is_even() # -> true

n |> is_odd() # -> false

import Integer

n = 12
n |> digits()
# → [1, 2]
n |> is_even()
# → "12"
n |> Integer.to_string()

Float

import Float
n = 10.3
# → 11.0
n |> Float.ceil()
# → 10.31
n |> ceil(2)
# → "1.030000+e01"
n |> Float.to_string()
n |> :erlang.float_to_binary([:compact, {:decimals, 2}])

# string_value = :erlang.float_to_binary(float_value, [:compact, {:decimals, 2}])

Operations

n = 10.3
abs(-3)
Float.round(n)
# remainder (modulo)
rem(11, 2)
# integer division
div(11, 2)

Type Casting

# → {34.1, ""}
Float.parse("34.1")
# → {34, ""}
Integer.parse("34")
# → "3.4100e+01"
Float.to_string(34.1)
# → "34.1"
Float.to_string(34.1, decimals: 2, compact: true)
:erlang.float_to_binary(34.1, [:compact, {:decimals, 2}])

float_value = 123.456
string_value = :erlang.float_to_binary(float_value, [:compact, {:decimals, 2}])

Map

# atom keys (:name)
m = %{name: "hi"}
# string keys ("name")
m = %{"name" => "hi"}
import Map
m = %{name: "hi"}
# key must exist as an atom
m = %{m | name: "yo"}
# → %{id: 2, name: "hi"}
m = m |> put(:id, 2)
# only if `id` doesn't exist (`||=`)
m = m |> put_new(:id, 3)
m = m |> put(:b, "Banana")
m = m |> merge(%{b: "Apple"})

m = m |> Map.update(:a, 100, &amp;(&amp;1 + 1))
m = m |> Map.update(:a, 0, fn a -> a + 1 end)
{c, u} = m |> Map.get_and_update(:a, &amp;({&amp;1, &amp;1 + 1} || {"default", "default"}))
# {c, u} = Map.get_and_update(m, :a, fn
#   nil -> {"default", "default"}
#   value -> {value, value + 1}
# end)

IO.inspect(c, label: "c")
IO.inspect(u, label: "u")
# IO.puts(u)
import Map

m = %{name: "jon"}
# m = m |> delete(:name)  # → %{}
# → {"John", %{}}
{name, m} = m |> pop(:name)

IO.inspect(name)
IO.inspect(m)

Reading

import Map

m = %{name: "jon", id: 1}

# → 1
IO.inspect(m |> get(:id))
# → [:id, :name]
IO.inspect(m |> keys())
# → [1, "hi"]
IO.inspect(m |> values())

m |> to_list()

Deep

import Map

m = %{name: "jon", id: 1}

# Ensure :b exists and is a map
m = Map.put(m, :b, %{})

# Now you can use Map.put_in/3
m = put_in(m, [:b, :c], "Banana")

# Now you can use Map.put_in/3
m = Map.replace(m, :b, %{c: "apple"})

users = %{
  john: %{age: 30},
  jane: %{age: 25}
}

{current_value, updated_users} = get_and_update_in(users, [:john, :age], &amp;{&amp;1, &amp;1 + 1})

# This will output: 30
IO.inspect(current_value)
IO.inspect(updated_users)

Constructing from lists

Map.new([{:b, 1}, {:a, 2}])
Map.new(a: 4, b: 6)
# → %{a: :a, b: :b}
Map.new([:a, :b], fn x -> {x, x} end)
defmodule AnyStruct do
  defstruct a: ""
end

Working with structs

import Map

# → %{a: "b"}
m = Map.from_struct(%AnyStruct{a: "b"})
IO.inspect(m)
# → User
%AnyStruct{}.__struct__

List

import List
l = [1, 2, 3, 4]
# push (append)
l = l ++ [5]
# unshift (prepend)
l = [0 | l]
l |> first()
l |> last()

nl = [[1, 2], [3, 4]]
nl |> flatten()
# l |> flatten(tail)
users = ["nsa", "tom", "jedi"]

Enum.map(users, fn user ->
  IO.puts("hello " <> user)
end)

Enum

Usage

import Enum
list = [:a, :b, :c]
# → :a
list |> at(0)
# → 3
list |> count()
# → false
list |> empty?()
# → true
list |> any?()
# → [:a, :b, :c, :d]
list |> concat([:d])

Map Reduce

numbers = [1, 2, 3, 4, 5]

sum =
  Enum.reduce(numbers, 0, fn number, acc ->
    acc + number
  end)

IO.puts(sum)
numbers = [1, 2, 3, 4, 5]

product =
  Enum.reduce(numbers, 1, fn number, acc ->
    acc * number
  end)

# This will output: 120
IO.puts(product)
strings = ["hello", "world", "elixir"]

uppercase_strings =
  Enum.map(strings, fn string ->
    String.upcase(string)
  end)

# This will output: ["HELLO", "WORLD", "ELIXIR"]
IO.inspect(uppercase_strings)
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

odd_numbers =
  Enum.reject(numbers, fn number ->
    rem(number, 2) == 0
  end)

# This will output: [1, 3, 5, 7, 9]
IO.inspect(odd_numbers)
strings = ["apple", "banana", "cherry", "date"]

has_long_string =
  Enum.any?(strings, fn string ->
    String.length(string) > 5
  end)

# This will output: true
IO.puts(has_long_string)
# Define a list
list1 = []
list2 = [1, 2, 3]

# Check if the lists are empty
is_list1_empty = Enum.empty?(list1)
is_list2_empty = Enum.empty?(list2)

# This will output: Is list1 empty? true
IO.puts("Is list1 empty? #{is_list1_empty}")
# This will output: Is list2 empty? false
IO.puts("Is list2 empty? #{is_list2_empty}")
[1, 2, 3, 4]
|> Enum.reduce(0, fn x, acc -> x + acc end)

Tuple

Tuples

import Tuple
t = {:a, :b}
# like tuple[1]
t |> elem(1)
t |> put_elem(1, :c)
t |> tuple_size()

Keyword Lists

list = [{:name, "John"}, {:age, 15}]
list[:name]
# For string-keyed keyword lists
list = [{"size", 2}, {"type", "shoe"}]
# → {"size", 2}
List.keyfind(list, "size", 0)

Functions

Lambdas

square = fn n -> n * n end
square.(2)

& syntax

square = &amp;(&amp;1 * &amp;1)
square.(8)

# square = &Math.square/1

Running

fun = fn n ->
  n
end

add = fn a, b ->
  a + b
end

fun.(2)

apply(add, [3, 4])

apply(Enum, :reverse, [[1, 2, 3]])

Function heads

defmodule Mymath do
  def join(a, b \\ nil), do: {a, b}
  def join(a, b) when is_nil(b), do: a
  def sum(a, b), do: a + b
end

IO.inspect(Mymath.join(3))
IO.inspect(Mymath.join(5, nil))
IO.inspect(Mymath.sum(10, 1))

# def join(a, b \\ nil)
# def join(a, b) when is_nil(b), do: a
# def sum(a, b), do: a + b
# def join(a, b) do: a <> b

Structs

defmodule User do
  defstruct name: "", age: nil
end
%User{name: "John", age: 20}

# → User
%User{}.__struct__

Protocols

Defining Protocols

defprotocol Blank do
  @doc "Returns true if data is considered blank/empty"
  def blank?(data)
end

defimpl Blank, for: List do
  def blank?([]), do: true
  def blank?(_), do: false
end

# → true
Blank.blank?([])

Any

defimpl Blank, for: Any do
  ...
end

defmodule User do
  # Falls back to Any
  @derive Blank
  defstruct name: ""
end

Comprehensions

For

for n <- [1, 2, 3, 4], do: n * n
for n <- 1..4, do: n * n
for {key, val} <- %{a: 10, b: 20}, do: val
# → [10, 20]
for {key, val} <- %{a: 10, b: 20}, into: %{}, do: {key, val * val}

Conditions

for n <- 1..10, rem(n, 2) == 0, do: n
# → [2, 4, 6, 8, 10]

Complex

for dir <- dirs,
    # nested comprehension
    file <- File.ls!(dir),
    # invoked
    path = Path.join(dir, file),
    # condition
    File.regular?(path) do
  IO.puts(file)
end

Misc

Meta Programming

__MODULE__
__MODULE__.__info__()

@after_compile __MODULE__
def __before_compile__(env)
def __after_compile__(env, _bytecode)
# invoked on `use`
def __using__(opts)

@on_definition {__MODULE__, :on_def}
def on_def(_env, kind, name, args, guards, body)

@on_load :load_check
def load_check

Regexp

exp = ~r/hello/
exp = ~r/hello/i
"hello world" =~ exp

Sigils

~r/regexp/
~w(list of strings)
~s|strings with #{interpolation} and \x20 escape codes|
~S|no interpolation and no escapes|
~c(charlist)

Type specs

@spec round(number) :: integer

@type number_with_remark :: {number, String.t()}
@spec add(number, number) :: number_with_remark

Behaviours

defmodule Parser do
  @callback parse(String.t()) :: any
  @callback extensions() :: [String.t()]
end

defmodule JSONParser do
  @behaviour Parser

  # ... parse JSON
  def parse(str), do: def(extensions, do: ["json"])
end