Introdução a Elixir


Vinícius Simões

$ whoami

  • Vinícius Simões
  • Software Engineer @ Podium
  • Github/Telegram/Twitter: @vinikira
  • LinkedIn: @vsimoes95
eu.jpg
podium.jpg
dory.jpg
moana.png
dory_moana.png

Conteúdo

  • O que é Elixir?
  • Sintaxe
  • Runtime
  • Ferramental

O que é Elixir

elixir_logo.png

Elixir é

  • Linguagem de programação brasileira
  • Funcional
  • Tipagem dinâmica e forte
  • Compilada
  • Concorrente
  • Paralela
  • Tolerante a falhas

Sintaxe

Olá mundo em Elixir

IO.puts("Hello, world")

Dissecando

IO.puts("Hello, world")

IO

Módulo

IO.puts("Hello, world")

puts

Função

IO.puts("Hello, world")

( )

Invocação da função

IO.puts("Hello, world")

"Hello, world!"

Argumento

Assinatura em Elixir: MFA (Modulo, Função e Aridade)

IO.puts("Hello, world")

IO.puts/1

Como escrevo e rodo um programa em Elixir?

Dado o arquivo meu_programa.exs

year_of_birth = IO.gets("Qual ano você nasceu? ")
year_of_birth = String.trim(year_of_birth)
year_of_birth = String.to_integer(year_of_birth)

today = Date.utc_today()

age = today.year - year_of_birth

IO.puts("Você tem #{age} anos.")
$ elixir meu_programa.exs

Equivalente em Node.js

Dado o arquivo meu_programa.js

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('Qual ano você nasceu? ', (answer) => {
  const birthYear = parseInt(answer);
  const currentYear = new Date().getFullYear();
  const age = currentYear - birthYear;
  console.log(`Você tem ${age} anos.`);
  rl.close();
});
$ node meu_programa.js

Tipos de dados básicos

1          # integer
0x1F       # integer
1.0        # float
true       # boolean
:atom      # atom / symbol
"elixir"   # string
[1, 2, 3]  # list
{1, 2, 3}  # tuple

Módulos

Módulos são uma forma de organizar funções são definidas com a palavra chave defmodule. Note que o escopo é definido como as palavras chaves do e end. Isso serve para funções e macros também.

defmodule MyModule do
  # code here
end

Funções

Funções são definidas dentro de módulos com a palavra-chave def.

defmodule MyModule do
  def greet(name) do
    IO.puts("Hello, #{name}")
  end
end

MyModule.greet("Vinícius") # => Hello, Vinícius!

Módulos mais complexos

Módulos agem como namespaces em um programa de Elixir

defmodule MyApp.Math do
  def add(n, n2) do
    n + n2
  end
end

defmodule MyApp.Main do
  def run() do
    MyApp.Math.add(1, 2)
  end
end

Operador pipe (bônus)

Sem múltiplas atribuições:

year_of_birth = String.to_integer(String.trim(IO.gets("Qual ano você nasceu? ")))

today = Date.utc_today()

age = today.year - year_of_birth

IO.puts("Você tem #{age} anos.")

O operador pipe (|>) serve para atribuir o retorno da última expressão como primeiro argumento da próxima.

year_of_birth =
  "Qual ano você nasceu? " # primeira expressão -> String
  |> IO.gets()             # segunda expressão  -> gets(resultado da primeira)
  |> String.trim()         # terceira expressão -> trim(resultado da segunda)
  |> String.to_integer()   # ultima expressão   -> to_integer(resultado da terceira)

today = Date.utc_today()

age = today.year - year_of_birth

IO.puts("Você tem #{age} anos.")

Pseudo código equivalente em JavaScript:

"Qual ano você nasceu"
  .gets()
  .trim()
  .to_integer()

Runtime

Onde Elixir roda?

Erlang

erlang_logo.png
  • Ericsson
  • Plataforma de telecomunicação
  • Erlang nasceu em 1986
  • Modelo de Atores

Processos

Processo é uma unidade de execução de código sequencial.

  • memória isolada
  • troca de mensagens
  • "caixa de e-mail"
  • coletor de lixos por processos
processos_erlang.png

Supervisor

Processos são tão baratos de serem criados em Elixir/Erlang que existem processos só para supervisionar outros processos.

supervisor_erlang.png
supervisor_erlang_2.png
supervisor_erlang.png

Concorrência

O modelo de concorrência do Erlang é baseado em um modelo preemptivo, que calcula o número de reduções que um processo teve e então o pausa para que outro possa usar a CPU.

concorrencia_erlang.png

Paralelismo

Aumenta o número de Schedulers por CPU.

paralelismo_erlang.png

Network

Um nó Erlang pode se conectar com outro via rede e trocar mensagens entre processos. Pode-se criar um cluster inteiro de nós Erlang.

network_erlang.png

Cases

WhatsApp

whatsapp_logo.png

https://blog.whatsapp.com/1-million-is-so-2011

2 milhões de usuários conectados em uma só máquina em 2012.

24 CPUS - 96 GB de memória - Intel Xeon x5670 @ 3.07Ghz

Uso de memória e CPU em 40%.

Discord

discord_logo.png

https://discord.com/blog/how-discord-scaled-elixir-to-5-000-000-concurrent-users

Discord usa cluster de Elixir/Erlang para servir 5 milhões de usuários.

Ferramental

Ex_Doc

Documentação em Elixir é um cidadão de primeira classe:

defmodule MyApp do
  @moduledoc """
  My app entrypoint
  """

  @doc """
  my function doc
  """
  def run() do
  end
end
iex_docs.png
ex_docs.png

Phoenix

Phoenix é o principal framework web do ecossistema.

  • MVC
  • API JSON
  • WebSocket
  • PubSub

Controller

defmodule HelloWeb.MyController do
  use Phoenix.Controller

  action_fallback HelloWeb.MyFallbackController

  def show(conn, %{"id" => id}, current_user) do
    with {:ok, post} <- fetch_post(id),
	 :ok <- authorize_user(current_user, :view, post) do
      render(conn, :show, post: post)
    end
  end
end

Router

defmodule HelloWeb.Router do
  use HelloWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, {HelloWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", HelloWeb do
    pipe_through :browser

    get "/", PageController, :home
  end

  # Other scopes may use custom stacks.
  # scope "/api", HelloWeb do
  #   pipe_through :api
  # end
  # ...
end

Ecto

Ecto é a biblioteca principal para lidar com dados no Elixir.

  • Banco de dados relacional
  • Validação de dados
  • Query builder
  • Repositório

Exemplo de mapeamento de tabela.

defmodule Weather do
  use Ecto.Schema

  # weather is the DB table
  schema "weather" do
    field :city,    :string
    field :temp_lo, :integer
    field :temp_hi, :integer
    field :prcp,    :float, default: 0.0
  end
end

Exemplo de Query

import Ecto.Query, only: [from: 2]

query = from u in User,
	  where: u.age > 18 or is_nil(u.email),
	  select: u

# Returns %User{} structs matching the query
Repo.all(query)

Absinthe

GraphQL toolkit para Elixir.

  • Integrado com Phoenix
  • Sem necessidade
  • DSL para definição do schema

Exemplo de definição de schema

defmodule BlogWeb.Schema do
  use Absinthe.Schema
  import_types BlogWeb.Schema.ContentTypes

  alias BlogWeb.Resolvers

  query do
    @desc "Get all posts"
    field :posts, list_of(:post) do
      resolve &Resolvers.Content.list_posts/3
    end
  end
end

LiveView

Extensão do Phoenix que permite criar páginas web dinâmicas sem escrevendo muito pouco ou quase nada de JavaScript.

  • componentes "React Like"
  • comunicação em tempo real
  • zero necessidade de API Rest ou GraphQL
  • lógica de tela é escrita em Elixir
  • SSR por padrão
defmodule TimelineLive do
  use Phoenix.LiveView

  def render(assigns) do
    render("timeline.html", assigns)
  end

  def mount(_, socket) do
    Twitter.subscribe("elixirphoenix")
    {:ok, assign(socket, :tweets, [])}
  end

  def handle_info({:new, tweet}, socket) do
    {:noreply,
     update(socket, :tweets, fn tweets ->
       Enum.take([tweet | tweets], 10)
     end)}
  end
end

Livebook

Livebook é um notebook interativo e colaborativo de Elixir.

livebook_preview.png

Perguntas?

Obrigado

thatsall.png