Elixir is a dynamic, functional language designed for building scalable and maintainable applications.
https://elixir-lang.org/install.html
https://github.com/Ironjanowar/TallerElixir
x = 1
# 1
1 = x
# 1
1 = y
# ** (CompileError) iex:3: undefined function y/0
2 = x
# ** (MatchError) no match of right hand side value: 1 ...
{a, b} = {1, 2}
# {1, 2}
a
# 1
{a, b} = {1, 2, 3}
# ** (MatchError) no match of right hand side value: {1, 2, 3} ...
{c, _, _} = {3, 3, 6}
# {3, 3, 6}
c
# 3
:ok
:hello
:this_is_an_atom
ThisIsAlsoAnAtom
Hello
case {1, 2, 3} do
{4, 5, 6} -> "Does not match"
{1, x, 3} -> "Match and bind x to 2"
_ -> "This will match any other value"
end
# "Match and bind x to 2"
Match the value of a variable
x = 2
case 2 do
^x -> "Will match"
_ -> "This will match any other value"
end
# "Will match"
Guards
case {1, 2, 3} do
{1, x, 3} when x > 0 -> "Will match"
_ -> "This will match any other value"
end
# "Will match"
Atoms are useful here
case some_function() do
{:ok, result} -> # Do something with result
{:error, error_message} -> # Do something with error_message
end
# le_test.ex
defmodule LeTest do
def sum(a, b) do
a + b
end
def sum({a, b}) do
a + b
end
end
# le_test.ex
defmodule LeTest do
# Arity 2
def sum(a, b), do: a + b
# Arity 1
def sum({a, b}), do: a + b
def sum(a), do: a + 1
def sum(_), do: 0
end
LeTest.sum(1, 2)
# 3
LeTest.sum(1)
# 2
LeTest.sum({2, 2})
# 4
Guards are very useful in functions
# le_test.ex
defmodule LeTest do
def sum(a, b) when is_integer(a) and is_integer(b), do: {:ok, a + b}
def sum(_, _), do: {:error, "I only accept integers"}
end
sum = fn a, b -> a + b end
# #Function<20.128620087/0 in :erl_eval.expr/5>
sum.(1, 2)
# 3
sum = & &1 + &2
sum.(2, 2)
# 4
Enum.map([1, 2, 3], fn x -> x + 1 end)
# [2, 3, 4]
Enum.map([1, 2, 3], & &1 + 1)
# [2, 3, 4]
[1, 2, 3] |> Enum.map(fn x -> x + 1 end)
# [2, 3, 4]
[1, 2, 3] |> Enum.map(fn x -> x + 1 end) |> Enum.sum()
# 9
[1, 2, 3]
|> Enum.map(fn x -> x + 1 end)
|> Enum.map(fn x -> x * 2 end)
|> Enum.sum()
|> Integer.to_string()
# "18"
spawn(fn -> nil end)
# #PID<0.214.0>
spawn(fn -> Enum.sum(1..100) end)
# #PID<0.220.0>
father = self()
spawn(fn ->
sum = Enum.sum(1..100)
send(father, sum)
end)
# #PID<0.231.0>
flush
# 5050
# :ok
father = self()
spawn(fn ->
sum = Enum.sum(1..100)
send(father, sum)
end)
receive do
num when is_integer(num) -> "Received the result #{num}"
_ -> "Wut?"
end
# "Received the result 5050"
pid =
spawn(fn ->
receive do
{pid, :ping} ->
send(pid, :pong)
end
end)
# #PID<0.245.0>
Process.alive?(pid)
# true
send(pid, {self(), :ping})
# {#PID<0.180.0>, :ping}
flush
# :pong
# :ok
Process.alive?(pid)
# false
defmodule PingPong do
def loop() do
receive do
{pid, :ping} ->
send(pid, :pong)
loop()
{pid, :pong} ->
send(pid, :ping)
loop()
end
end
end
defmodule PingPong do
def loop(), do: loop({0, 0})
def loop({pings, pongs} = state) do
receive do
{pid, :ping} ->
send(pid, :pong)
loop({pings + 1, pongs})
{pid, :pong} ->
send(pid, :ping)
loop({pings, pongs + 1})
{pid, :state} ->
send(pid, state)
loop(state)
end
end
end
self()
# #PID<0.101.0>
pid = spawn(fn -> receive do :crash -> 1/0 end end)
# #PID<0.140.0>
Process.link(pid)
# true
send(pid, :crash)
# 18:37:21.684 [error] Process #PID<0.140.0> raised an exception
# ** (ArithmeticError) bad argument in arithmetic expression
# :erlang./(1, 0)
self()
# #PID<0.115.0>
Handle crashes, the magic :trap_exit
self()
# #PID<0.115.0>
Process.flag(:trap_exit, true)
# false
pid = spawn(fn -> receive do :crash -> 1/0 end end)
# #PID<0.140.0>
Process.link(pid)
# true
send(pid, :crash)
# 18:37:21.684 [error] Process #PID<0.140.0> raised an exception
# ** (ArithmeticError) bad argument in arithmetic expression
# :erlang./(1, 0)
self()
# #PID<0.115.0>
flush
# {:EXIT, #PID<0.140.0>, {:badarith, [{:erlang, :/, [1, 0], []}]}}
# :ok
{pid, _} = spawn_monitor(fn -> receive do :crash -> 1/0 end end)
# {#PID<0.109.0>, #Reference<0.1454498977.3767533571.36942>}
send(pid, :crash)
# 01:04:55.908 [error] Process #PID<0.112.0> raised an exception
# ** (ArithmeticError) bad argument in arithmetic expression
# :erlang./(1, 0)
A GenServer is a process like any other Elixir process and it can be used to keep state, execute code asynchronously and so on.
defmodule Stack do
use GenServer
# Callbacks
def init(stack) do
{:ok, stack}
end
def handle_call(:pop, _from, [head | tail]) do
{:reply, head, tail}
end
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
end
{:ok, pid} = GenServer.start_link(Stack, [:hello])
# {:ok, #PID<0.129.0>}
state: [:hello]
GenServer.cast(pid, {:push, :world})
# :ok
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
GenServer.call(pid, :pop)
# :world
def handle_call(:pop, _from, [head | tail]) do
{:reply, head, tail}
end
def handle_call(message, from, state) do
# Replies
{:reply, answer, new_state}
{:noreply, new_state}
{:stop, reason, reply, new_state}
{:stop, reason, reply}
end
def handle_cast(message, state) do
# Replies
{:noreply, new_state}
{:stop, reason}
end
defmodule Stack do
use GenServer
# Client
def start(initial_state) do
GenServer.start_link(Stack, initial_state)
end
def push(pid, item) do
GenServer.cast(pid, {:push, item})
end
def pop(pid) do
GenServer.call(pid, :pop)
end
# Callbacks ...
end
defmodule Stack do
use GenServer
# Client
def start(initial_state \\ []) do
GenServer.start_link(Stack, initial_state, name: Stack)
end
def push(item) do
GenServer.cast(Stack, {:push, item})
end
def pop() do
GenServer.call(Stack, :pop)
end
# Callbacks ...
end
The Agent module provides a basic server implementation that allows state to be retrieved and updated via a simple API.
defmodule Basket do
use Agent
def start() do
Agent.start_link(&MapSet.new/0, name: __MODULE__)
end
def member?(product) do
Agent.get(__MODULE__, fn state -> MapSet.member?(state, product) end)
end
def get() do
Agent.get(__MODULE__, fn state -> state end)
end
def put(product) do
Agent.update(__MODULE__, fn state -> MapSet.put(state, product) end)
end
def delete(product) do
Agent.update(__MODULE__, fn state -> MapSet.delete(state, product) end)
end
end
defmodule Basket do
use Agent
def start() do
Agent.start_link(&MapSet.new/0, name: __MODULE__)
end
def member?(product) do
Agent.get(__MODULE__, &MapSet.member?(&1, product))
end
def get() do
Agent.get(__MODULE__, &(&1 |> MapSet.to_list()))
end
def put(product) do
Agent.update(__MODULE__, &MapSet.put(&1, product))
end
def delete(product) do
Agent.update(__MODULE__, &(MapSet.delete(&1, product) |> MapSet.to_list()))
end
end
$ mix new awesome_bot
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/awesome_bot.ex
* creating test
* creating test/test_helper.exs
* creating test/awesome_bot_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd awesome_bot
mix test
Run "mix help" for more commands.
https://github.com/rockneurotiko/ex_gram
# awesome_bot/mix.exs
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:ex_gram, "0.5.0-rc3"}
]
end
https://github.com/rockneurotiko/ex_gram
# awesome_bot/mix.exs
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:ex_gram, git: "git://github.com/rockneurotiko/ex_gram.git"}
]
end
defmodule AwesomeBot do
use Application
import Supervisor.Spec
require Logger
def start(_type, _args) do
token = ExGram.Config.get(:ex_gram, :token)
children = [
supervisor(ExGram, []),
supervisor(Awesome.Bot, [:polling, token])
]
opts = [strategy: :one_for_one, name: AwesomeBot]
case Supervisor.start_link(children, opts) do
{:ok, _} = ok ->
Logger.info("Starting AwesomeBot")
ok
error ->
Logger.error("Error starting AwesomeBot")
error
end
end
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
mod: {AwesomeBot, []}
]
end
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
config :ex_gram,
token: "token"
defmodule AwesomeBot.Bot do
@bot :awesome_bot
use ExGram.Bot,
name: @bot
def bot(), do: @bot
def handle({:command, "start", _msg}, context) do
msg = "Well hello! This is an awesome bot!"
answer(context, msg)
end
end
$ mix run --no-halt
Compiling 1 file (.ex)
23:57:44.219 [info] Starting AwesomeBot