Skip to content

Instantly share code, notes, and snippets.

@jagdeepsingh
Last active October 18, 2019 23:35
Show Gist options
  • Save jagdeepsingh/b5a5a60d91c959a4dc9eb578d052c935 to your computer and use it in GitHub Desktop.
Save jagdeepsingh/b5a5a60d91c959a4dc9eb578d052c935 to your computer and use it in GitHub Desktop.
Elixir

Binary

A binary is a bitstring where number of bits is divisible by 8.

> binary = <<0, 1, 2>>
> byte_size binary
3

> String.valid? binary
true

> binary <> <<4, 5>>
<<0, 1, 2, 4, 5>>

> <<x, y, z>> = binary
> x
0
> y
1
> z
2

> <<x, 1, y :: binary>> = <<3, 1, 2, 4, 5>>
> x
3
> y
<<2, 4, 5>>
> string = "hełło"
> string <> <<0>>
<<104, 101, 197, 130, 197, 130, 111, 0>>

> <<259 :: utf8>>
"ă"

> to_string 'hełło'
'hełło'
> to_charlist "hełło"
[104, 101, 322, 322, 111]
> to_string to_charlist 'hełło'
"hełło"
> to_string 1
"1"

Agent

> {:ok, agent} = Agent.start_link fn -> [] end
{:ok, #PID<0.108.0>}

> Agent.update(agent, fn list -> ["eggs" | list] end)
:ok

> Agent.get(agent, fn list -> list end)
["eggs"]

> Agent.stop(agent)
:ok

References

Erlang libraries

Read more

Create a new application

$ mix new kv --module KV

lib/kv/bucket.ex

defmodule KV.Bucket do
  @doc """
  Starts a new bucket.
  """
  def start_link do
    Agent.start_link(fn -> %{} end)
  end

  def get(bucket, key) do
    Agent.get(bucket, &Map.get(&1, key))
  end

  def put(bucket, key, value) do
    Agent.update(bucket, &Map.put(&1, key, value))
  end

  def delete(bucket, key) do
    Agent.get_and_update(bucket, &Map.pop(&1, key))
  end
end

Start/Stop applications

Run console by typing iex -S mix run --no-start.

> Application.start(:kv)
:ok
> Application.stop(:kv)
:ok
> Application.ensure_all_started(:kv)
{:ok, [:logger, :kv]}

Application callbacks

# mix.exs
defmodule KV.Mixfile do
  def application do
    [applications: [:logger],
     mod: {KV, []}]
  end
end
# lib/kv.ex
defmodule KV do
  use Application
  
  def start(_type, _args) do
    KV.Supervisor.start_link
  end
end

Run console iex -S mix.

> KV.Registry.create(KV.Registry, "shopping")
:ok
> KV.Registry.lookup(KV.Registry, "shopping")
{:ok, #PID<0.88.0>}

GenServer

# lib/kv/registry.ex
defmodule KV.Registry do
  use GenServer

  ## Client API

  @doc """
  Starts the registry.
  """
  def start_link do
    GenServer.start_link(__MODULE__, :ok, [])
  end

  @doc """
  Looks up the bucket pid for `name` stored in `server`.

  Returns `{:ok, pid}` if the bucket exists, `:error` otherwise.
  """
  def lookup(server, name) do
    GenServer.call(server, {:lookup, name})
  end

  @doc """
  Ensures there is a bucket associated to the given `name` in `server`.
  """
  def create(server, name) do
    GenServer.cast(server, {:create, name})
  end

  @doc """
  Stops the registry.
  """
  def stop(server) do
    GenServer.stop(server)
  end

  ## Server callbacks

  def init(:ok) do
    names = %{}
    refs = %{}
    {:ok, {names, refs}}
  end

  def handle_call({:lookup, name}, _from, {names, _} = state) do
    {:reply, Map.fetch(names, name), state}
  end

  def handle_cast({:create, name}, {names, refs}) do
    if Map.has_key?(names, name) do
      {:noreply, {names, refs}}
    else
      {:ok, pid} = KV.Bucket.start_link
      ref = Process.monitor(pid)
      refs = Map.put(refs, ref, name)
      names = Map.put(names, name, pid)
      {:noreply, {names, refs}}
    end
  end

  def handle_info({:DOWN, ref, :process, _pid, _reason}, {names, refs}) do
    {name, refs} = Map.pop(refs, ref)
    names = Map.delete(names, name)
    {:noreply, {names, refs}}
  end

  def handle_info(_msg, state) do
    {:noreply, state}
  end
end
> {:ok, pid} = KV.Bucket.start_link
{:ok, #PID<0.66.0>}
> Process.monitor(pid)
#Reference<0.0.0.551>
> Agent.stop(pid)
:ok
> flush()
{:DOWN, #Reference<0.0.0.551>, :process, #PID<0.66.0>, :normal}
# test/kv/registry_test.exs
defmodule KV.RegistryTest do
  use ExUnit.Case, async: true

  setup do
    {:ok, registry} = KV.Registry.start_link
    {:ok, registry: registry}
  end

  test "spawns buckets", %{registry: registry} do
    assert KV.Registry.lookup(registry, "shopping") == :error

    KV.Registry.create(registry, "shopping")
    assert {:ok, bucket} = KV.Registry.lookup(registry, "shopping")

    KV.Bucket.put(bucket, "milk", 1)
    assert KV.Bucket.get(bucket, "milk") == 1
  end

  test "removes bucket on exit", %{registry: registry} do
    KV.Registry.create(registry, "shopping")
    {:ok, bucket} = KV.Registry.lookup(registry, "shopping")
    Agent.stop(bucket)
    assert KV.Registry.lookup(registry, "shopping") == :error
  end
end

Supervisor

one_for_one

defmodule KV.Supervisor do
  use Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
    children = [
      worker(KV.Registry, [KV.Registry])
    ]

    supervise(children, strategy: :one_for_one)
  end
end
> KV.Supervisor.start_link
{:ok, #PID<0.126.0>}
> KV.Registry.create(KV.Registry, "shopping")
:ok
> KV.Registry.lookup(KV.Registry, "shopping")
{:ok, #PID<0.130.0>}

simple_one_for_one

# lib/kv/bucket/supervisor.ex
defmodule KV.Bucket.Supervisor do
  use Supervisor

  @name KV.Bucket.Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, :ok, name: @name)
  end

  def start_bucket do
    Supervisor.start_child(@name, [])
  end

  def init(:ok) do
    children = [
      worker(KV.Bucket, [], restart: :temporary)
    ]

    supervise(children, strategy: :simple_one_for_one)
  end
end

Run console iex -S mix.

> KV.Bucket.Supervisor.start_link
{:ok, #PID<0.130.0>}
> {:ok, bucket} = KV.Bucket.Supervisor.start_bucket
{:ok, #PID<0.132.0>}
> KV.Bucket.put(bucket, "eggs", 3)
:ok
> KV.Bucket.get(bucket, "eggs")
3
# lib/kv/registry.ex
defmodule KV.Registry do
  use GenServer

  ...
  def handle_cast({:create, name}, {names, refs}) do
    if Map.has_key?(names, name) do
      {:noreply, {names, refs}}
    else
      {:ok, pid} = KV.Bucket.Supervisor.start_bucket
      ref = Process.monitor(pid)
      refs = Map.put(refs, ref, name)
      names = Map.put(names, name, pid)
      {:noreply, {names, refs}}
    end
  end
  ...
end
# test/kv/registry_test.exs
defmodule KV.RegistryTest do
  use ExUnit.Case, async: true
  
  setup context do
    {:ok, registry} = KV.Registry.start_link(context.test)
    {:ok, registry: registry}
  end
  
  test "removes bucket on crash", %{registry: registry} do
    KV.Registry.create(registry, "shopping")
    {:ok, bucket} = KV.Registry.lookup(registry, "shopping")

    Process.exit(bucket, :shutdown)

    ref = Process.monitor(bucket)
    assert_receive {:DOWN, ^ref, _, _, _}

    assert KV.Registry.lookup(registry, "shopping") == :error
  end
end

Supervision tree

# lib/kv/supervisor.ex
defmodule KV.Supervisor do
  use Supervisor

  ...
  def init(:ok) do
    children = [
      worker(KV.Registry, [KV.Registry]),
      supervisor(KV.Bucket.Supervisor, [])
    ]

    supervise(children, strategy: :rest_for_one)
  end
end

Compile

$ cd mix
$ mix compile

Run console

$ iex -S mix

Run tests

test/kv/bucket_test.exs

defmodule KV.BucketTest do
  use ExUnit.Case, async: true

  setup do
    {:ok, bucket} = KV.Bucket.start_link
    {:ok, bucket: bucket}
  end

  test "sample" do
    assert foo == nil
  end

  test "sample", %{bucket: bucket} do
    # `bucket` is now the bucket from the setup block
  end
end

Use setup with context.

defmodule KV.RegistryTest do
  use ExUnit.Case, async: true

  setup context do
    {:ok, registry} = KV.Registry.start_link(context.test)
    {:ok, registry: registry}
  end
end
$ mix test
$ mix test test/kv.exs
$ mix test test/kv.exs:5

Umbrella project

Create new umbrella project.

mix new kv_umbrella --umbrella

Add application to umbrella project.

cd kv_umbrella/apps
mix new kv_server --module KVServer --sup

Passing the --sup flag will tell Mix to generate a supervision tree automatically for us.

Go to root of umbrella application and run mix test to run tests for all the applications.

Adding a dependency to other application

# apps/kv_server/mix.exs
defp deps do
  [{:kv, in_umbrella: true}]
end

Also, update project method to change build_path, config_path, deps_path, lockfile, in apps/kv/mix.exs to match their values in other applications.

defmodule KV.Mixfile do
  use Mix.Project
  
  def project do
    [app: :kv,
     version: "0.1.0",
     elixir: "~> 1.3",
     build_path: "../../_build",
     config_path: "../../config/config.exs",
     deps_path: "../../deps",
     lockfile: "../../mix.lock",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end
end

gen_tcp

Lets implement an echo server.

# apps/kv_server/lib/kv_server.ex
defmodule KVServer do
  require Logger

  def accept(port) do
    {:ok, socket} = :gen_tcp.listen(port,
                      [:binary, packet: :line, active: false, reuseaddr: true])
    Logger.info "Accepting connections on port #{port}"
    loop_acceptor(socket)
  end

  defp loop_acceptor(socket) do
    {:ok, client} = :gen_tcp.accept(socket)
    serve(client)
    loop_acceptor(socket)
  end

  defp serve(socket) do
    socket
    |> read_line()
    |> write_line(socket)

    serve(socket)
  end

  defp read_line(socket) do
    {:ok, data} = :gen_tcp.recv(socket, 0)
    data
  end

  defp write_line(line, socket) do
    :gen_tcp.send(socket, line)
  end
end

Start an IEx session inside app kv_server by running iex -S mix.

iex> KVServer.accept(40400)

In new terminal, test the server using telnet.

$ telnet 127.0.0.1 4040
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
hello

Add Supervisor to echo server

# lib/kv_server/application.ex
defmodule KVServer.Application do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec

    children = [
      worker(Task, [KVServer, :accept, [4040]])
    ]

    opts = [strategy: :one_for_one, name: KVServer.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Start the server by typing mix run --no-halt in the terminal and test it using telnet. Now, even if the client gets killed, whole server will still crash, but a new one will start right away.

If you try to connent multiple clients to this server at the same time, it won't work.

Support multiple clients

# apps/kv_server/lib/kv_server/application.ex
def start(_type, _args) do
  import Supervisor.Spec

  children = [
    supervisor(Task.Supervisor, [[name: KVServer.TaskSupervisor]]),
    worker(Task, [KVServer, :accept, [4040]])
  ]

  opts = [strategy: :one_for_one, name: KVServer.Supervisor]
  Supervisor.start_link(children, opts)
end
# apps/kv_server/lib/kv_server.ex
defp loop_acceptor(socket) do
  {:ok, client} = :gen_tcp.accept(socket)
  {:ok, pid} = Task.Supervisor.start_child(KVServer.TaskSupervisor, fn -> serve(client) end)
  :ok = :gen_tcp.controlling_process(client, pid)
  loop_acceptor(socket)
end

Running mix run --no-halt will now respond to multiple clients.

References

Read more

$ iex
> case {1, 2, 3} do
> {4, 5, 6} ->
> "This won't match"
> {1, x, 3} ->
> "This clause will match with x = #{x}"
> _ ->
> "This will match anything"
> end
"This clause will match with x = 2"
> case {1, 2, 3} do
> {1, x, 3} when x < 0 ->
> "Matched"
> _ ->
> "Not matched"
> end
"Not matched"
> cond do
> 2 + 2 == 5 ->
> "Not true"
> 2 * 2 == 4 ->
> "True"
> 1 + 1 == 2 ->
> "True again"
> true ->
> "This is always true"
> end
"True"
> if true do
> "This works"
> end
> unless true do
> "This won't be seen"
> end
> if true, do: :this, else: :that
:this
> if false, do: :this, else: :that
:that
> if true, do: (
> a = 1 + 2
> a + 10
> )
13
> is_number(if true do
> 1 + 2
> end)
true
> Ctrl + C
$ iex
> list = [1, 2, 3]
> double = fn x -> x * 2 end
> Enum.map(list, double)
[2, 4, 6]
> map = %{ 1 => 2, 3 => 4 }
> cross = fn {k, v} -> k * v end
> Enum.map(map, cross)
[2, 12]
> range = 1..5
> Enum.map(range, double)
[2, 4, 6, 8, 10]
> range = 1..5
> foo = &+/2
> Enum.reduce(range, 0, foo)
15
> odd? = &(rem(&1, 2) != 0)
> range = 1..5
> Enum.filter(range, odd?)
[1, 3, 5]
> list = [1, 2, 3, 4]
> for n <- list, do: n * n
[1, 4, 9, 16]
> values = [good: 1, good: 2, bad: 3, good: 4]
> for {:good, n} <- values, do: n * n
[1, 4, 16]
> multiple_of_3? = fn(n) -> rem(n, 3) == 0 end
> for n <- 0..5, multiple_of_3?.(n), do: n * n
[0, 9]
> Ctrl + C
$ vi triple.exs
defmodule Triple do
def pythagorean(n) when n > 0 do
for a <- 1..n,
b <- 1..n,
c <- 1..n,
a + b + c <= n,
a * a + b * b == c * c,
do: {a, b, c}
end
end
$ iex triple.exs
> Triple.pythagorean(5)
[]
> Triple.pythagorean(12)
[{3, 4, 5}, {4, 3, 5}]
> Ctrl + C
$ iex
> {:ok, file} = File.open "hello", [:write]
{:ok, #PID<0.82.0>}
> IO.binwrite file, "world"
:ok
> File.close file
:ok
> File.read "hello"
{:ok, "world"}
> File.read! "hello"
"world"
> File.read "wrong_file_name"
{:error, :enoent}
> File.read! "wrong_file_name"
** (File.Error) could not read file ...
> Ctrl + C
$ iex
> add = fn a, b -> a + b end
#Function<12.52032458/2 in :erl_eval.expr/5>
> is_function(add)
true
> is_function(add, 2)
true
> is_function(add, 1)
false
> add.(2, 3)
5
> double = fn a -> add.(a, a) end
> double.(3)
6
> f = fn
> x, y when x > 0 -> x + y
> x, y -> x * y
> end
> f.(2, 3)
5
> f.(-2, 3)
-6
> g = fn
> x, y -> x + y
> x, y when x < 0 -> x * y
> end
> g.(2, 3)
5
> g.(-2, 3)
-6
> fun = &(&1 + 3)
> fun.(4)
7
> odd? = &(rem(&1, 2) != 0)
> odd?.(3)
true
> fun = &List.flatten(&1, &2)
> fun.([1, [2, [3]]], [4, [5, [6]]])
[1, 2, 3, 4, [5, [6]]]
> Ctrl + C
$ iex
> ary = [1, 2, true, 3]
> length ary
4
> ary2 = [3, 4, 5]
> ary ++ ary2
[1, 2, true, 3, 3, 4, 5]
> ary = [1, true, 2, false, 3, true]
> ary2 = [true, false]
> ary -- ary2
[1, 2, 3, true]
> hd ary
1
> tl ary
[true, 2, false, 3, true]
> ary = [104, 101, 108, 108, 111]
'hello'
> hd ary
104
> tl ary
'ello'
> list = [1 | [2 | [3 | []]]]
[1, 2, 3]
> [0 | list]
[0, 1, 2, 3]
> [a, b, c] = [1, 2, 3]
> a
1
> b
2
> [head | tail] = [1, 2, 3]
> head
1
> tail
[2, 3]
> [h | _] = [1, 2, 3]
> h
1
> charlist = 'hełło'
[104, 101, 322, 322, 111]
> is_list charlist
true
> 'hello'
'hello'
> list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]
> list == [a: 1, b: 2]
true
> list ++ [c: 3]
[a: 1, b: 2, c: 3]
> [a: 0] ++ list
[a: 0, a: 1, b: 2]
> list[:a]
1
> list = [a: 0] ++ list
> list[:a]
0
> Ctrl + C
$ iex
> map = %{a: 1, b: 2}
> map[:a]
1
> map[:c]
nil
> %{} = map
%{a: 1, b: 2}
> %{b: b} = map
> b
2
> n = 1
> map = %{n => :one}
%{1 => :one}
> map[n]
:one
> %{^n => :one} = map
%{1 => :one}
> map = %{a: 1, b: 2}
> Map.get(map, :b)
2
> Map.to_list map
[a: 1, b: 2]
> map.b
2
> %{map | a: 2}
%{a: 2, b: 2}
> users = [john: %{name: "John", age: 27}, mary: %{name: "Mary", age: 29}]
> users[:john].age
27
> users = put_in users[:john].age, 31
> users
[john: %{age: 31, name: "John"}, mary: %{age: 29, name: "Mary"}]
> Ctrl + C
$ iex
> defmodule Math do
> def sum(a, b) do
> a + b
> end
> end
> Math.sum 2, 3
5
> Ctrl + C
$ vi math.ex
defmodule Math do
def sum(a, b) do
a + b
end
end
$ elixirc math.ex
$ iex
> Math.sum 3, 4
7
> Ctrl + C
$ vi math.exs
defmodule Math do
def sum(a, b) do
do_sum(a, b)
end
defp do_sum(a, b) do
a + b
end
end
IO.puts Math.sum 3, 4
IO.puts Math.do_sum 4, 5
$ elixir math.exs
7
UndefinedFunctionError
$ vi math.exs
defmodule Math do
def zero?(0) do
true
end
def zero?(x) when is_integer(x) do
false
end
end
IO.puts Math.zero? 0
IO.puts Math.zero? 1
IO.puts Math.zero? '1'
$ elixir math.exs
true
false
FunctionClauseError
$ vi math.exs
defmodule Math do
def zero?(0), do: true
def zero?(x) when is_integer(x), do: false
end
IO.puts MathBox.zero? 0
IO.puts MathBox.zero? 1
$ elixir math.exs
true
false
$ iex math.exs
> Math.zero? 0
true
> fun = &Math.zero?/1
> is_function(fun)
true
> fun.(1)
false
> (&is_function/1).(fun)
true
> Ctrl + C
$ vi concat.exs
defmodule Concat do
def join(a, b, sep \\ " ") do
a <> sep <> b
end
end
IO.puts Concat.join("Hello", "world")
IO.puts Concat.join("Hello", "world", ", ")
$ elixir concat.exs
Hello world
Hello, world
$ vi default_test.exs
defmodule DefaultTest do
def dowork(x \\ IO.puts "Hello") do
x
end
end
$ iex default_test.exs
> DefaultTest.dowork
Hello
:ok
> DefaultTest.dowork 3
3
> DefaultTest.dowork
Hello
:ok
> Ctrl + C
$ vi concat.exs
defmodule Concat do
def join(a, b \\ nil, sep \\ " ")
def join(a, b, _sep) when is_nil(b), do: a
def join(a, b, sep) do
a <> sep <> b
end
end
IO.puts Concat.join("Hello", "world")
IO.puts Concat.join("Hello", "world", ", ")
IO.puts Concat.join("Hello")
$ elixir concat.exs
Hello world
Hello, world
Hello
$ iex
> Path.join("foo", "bar")
"foo/bar"
> Path.join("foo", "bar") |> Path.join("baz")
"foo/bar/baz"
> Path.expand("~/foo/bar")
"/Users/vinsol/foo/bar"
> Ctrl + C
$ iex
> self()
#PID<0.80.0>
> Process.alive?(self())
true
> pid = spawn fn -> 1 + 2 end
#PID<0.84.0>
> Process.alive? pid
false
> send self(), {:hello, "world"}
{:hello, "world"}
> receive do
> {:hello, msg} -> msg
> {:world, msg} -> "won't match"
> end
"world"
> receive do
> {:hello, msg} -> msg
> after
> 1_000 -> "nothing after 1s"
> end
> send self(), :hello
> send self(), :world
> flush()
:hello
:world
:ok
> Ctrl + C
$ iex
> range = 1..100_000
> thrice = &(&1 * 3)
> odd? = &(rem(&1, 2) != 0)
> range |> Stream.map(thrice) |> Stream.filter(odd?)
#Stream<[enum: 1..100000,
funs: [#Function<46.87278901/1 in Stream.map/2>,
#Function<39.87278901/1 in Stream.filter/2>]]>
> stream = Stream.cycle([1, 2, 3])
> Enum.take(stream, 10)
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
> str = "hełło"
> String.next_codepoint str
{"h", "ełło"}
> stream = Stream.unfold(str, &String.next_codepoint/1)
> Enum.take(stream, 3)
["h", "e", "ł"]
> stream = File.stream!("recursion.exs")
%File.Stream{line_or_bytes: :line, modes: [:raw, :read_ahead, :binary],
path: "recursion.exs", raw: true}
> Enum.take(stream, 5)
["defmodule Recursion do\n", " def print_n_times(msg, n) when n <= 1 do\n",
" IO.puts msg\n", " end\n", "\n"]
> Ctrl + C
$ iex
> defmodule User do
> defstruct name: 'John', age: 27
> end
> %User{}
%User{age: 27, name: 'John'}
> %User{name: 'Meg'}
%User{age: 27, name: 'Meg'}
> john = %User{}
> john.name
'John'
> meg = %{john | name: 'Meg'}
%User{age: 27, name: 'Meg'}
> %User{name: name} = john
> name
'John'
> john.__struct__
User
> Map.keys(john)
[:__struct__, :age, :name]
> kurt = Map.put(%User{}, :name, 'Kurt')
%User{age: 27, name: 'Kurt'}
> Map.merge(kurt, john)
%User{age: 27, name: 'John'}
> defmodule Car do
> @enforce_keys [:make]
> defstruct [:model, :make]
> end
> %Car{}
** (ArgumentError) the following keys must also be given when building struct Car: [:make]
> %Car{make: 'Honda'}
%Car{make: 'Honda', model: nil}
> Ctrl + C
$ vi k_v.exs
defmodule KV do
def start_link do
Task.start_link(fn -> loop(%{}) end)
end
defp loop(map) do
receive do
{:get, key, caller} ->
send caller, Map.get(map, key)
loop(map)
{:put, key, value} ->
loop(Map.put(map, key, value))
end
end
end
$ iex k_v.exs
> {:ok, pid} = KV.start_link
> send pid, {:get, :hello, self()}
> flush()
nil
:ok
> send pid, {:put, :hello, :world}
> send pid, {:get, :hello, self()}
> flush()
:world
:ok
> Process.register(pid, :kv)
> send :kv, {:get, :hello, self()}
> flush()
:world
:ok
> Ctrl + C
$ iex
> h div/2
Documentation for method `div` that takes 2 arguments.
> raise "This error will be raised"
** (RuntimeError) This error will be raised
> 1 = x
** (CompileError) iex:42: undefined function x/0
> x = 1
1
> 1 = x
1
> 2 = x
** (MatchError) no match of right hand side value: 1
> 2 = x + 1
2
> x = 1
> ^x = 1
1
> ^x = 2
** (MatchError) no match of right hand side value: 2
> {y, ^x} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}
> {y, ^x} = {2, 1}
{2, 1}
> y
2
> x
1
> {x, x} = {1, 1}
{1, 1}
> {x, x} = {1, 2}
** (MatchError) no match of right hand side value: {1, 2}
> range = 1..100_000
> thrice = &(&1 * 3)
> odd? = &(rem(&1, 2) != 0)
> range |> Enum.map(thrice) |> Enum.filter(odd?) |> Enum.sum
7500000000
> defprotocol Size do
> def size(data)
> end
> defimpl Size, for: BitString do
> def size(binary), do: byte_size(binary)
> end
> Size.size("foo")
3
> defimpl Size, for: Map do
> def size(map), do: map_size(map)
> end
> Size.size(%{label: "some label"})
1
> defmodule User do
> defstruct [:name, :age]
> end
> defimpl Size, for: User do
> def size(_user), do: 2
> end
> Size.size(%User{})
2
> defimpl Size, for: Any do
> def size(_), do: 0
> end
> defmodule Person do
> @derive [Size]
> defstruct [:name, :age]
> end
> Size.size(%Person{})
0
> defprotocol Size do
> @fallback_to_any true
> def size(data)
> end
> defimpl Size, for: Any do
> def size(_), do: 0
> end
> Size.size(2)
0
> Ctrl + C
$ vi greetings.exs
IO.puts "Hello world from Elixir"
$ elixir greetins.exs
Hello world from Elixir
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment