ELIXIR

QUE POÇÃO É ESSA?

MATA56 - Paradigmas de Linguagens de Programação

16/05/2016

Alesson Bruno, Cristiano Santos e Felipe Rabello

HISTÓRIA

ERLANG?

TEMPO REAL

CONCORRÊNCIA

DISTRIBUÍDO

TOLERÂNCIA A FALHAS

TEMPO REAL

CONCORRÊNCIA

DISTRIBUÍDO

TOLERÂNCIA A FALHAS

"FEIA"

DESDE 1996

2012

- DIFERENTES CONSTRUTORES

- EXCELENTE FERRAMENTAL

- METAPROGRAMAÇÃO

- POLIMORFISMO (PROTOCOLS)

- MACROS

- E MAIS ...

LICENÇA

Direitos reservados da Plataformatec

Código fonte: Apache 2 License

E POR QUE ELIXIR?

ZILHÕES DE TRANSISTORES

MUITOS NÚCLEOS

COMO MANTÊ-LOS OCUPADOS?

EXPLORANDO TRABALHO

E POR QUE ELIXIR?

FUNCIONAL E CONCORRENTE

FUNCIONAL NÃO PRECISA SER MATEMÁTICO

NEM COMPLEXO

E POR QUE ELIXIR?

FUNCIONAL E CONCORRENTE

CONCORRENTE SEM ABSTRAÇÕES COMO:

LOCKS E SEMÁFOROS

PARADIGMAS SUPORTADOS

MULTI PARADIGMA

FUNCIONAL, CONCORRENTE, DISTRIBUÍDO E

ORIENTADO A PROCESSOS

COMPILADA, INTERPRETADA OU HÍBRIDA?

COMPILADA PARA O BYTECODE DA MÁQUINA DO ERLANG (BEAM)

GERENCIAMENTO DE MEMÓRIA

ERLANG POSSUI GARBAGE COLLECTOR (GC)

ELIXIR RODA EM CIMA DA MÁQUINA DO ERLANG

ELIXIR TAMBÉM POSSUI GC

GERENCIAMENTO DE MEMÓRIA

MILHARES DE PROCESSOS CONCORRENTES

PROCESSOS ISOLADOS COM GC INDEPENDENTE

REDUZ PAUSAS

UTILIZA MAIS RECURSOS DA MÁQUINA EFICIENTEMENTE

SELEÇÃO

CASE, COND E IF

SELEÇÃO

CASE

case {1, 2, 3} do
    {4, 5, 6} ->
        "This clause won't match"
    {1, x, 3} ->
        "This clause will match and bind x to 2 in this clause"
    _ ->
        "This clause would match any value"
end

# output
"This clause will match and bind x to 2 in this clause"

SELEÇÃO

COND

cond do
    2 + 2 == 5 ->
        "This will not be true"
    2 * 2 == 3 ->
        "Nor this"
    1 + 1 == 2 ->
        "But this will"
end

# output
"But this will"

SELEÇÃO

IF E UNLESS

if true do
    "This works!"
end

# output
"This works!"

unless true do
    "This will never be seen"
end

REPETIÇÃO

LOOPS EM ELIXIR (ASSIM COMO EM OUTRA LINGUAGEM FUNCIONAL) SÃO ESCRITOS DE FORMA DIFERENTE DAS LINGUAGENS IMPERATIVAS

USAR RECURSÃO, REDUCE, MAP, ...

REPETIÇÃO

RECURSÃO

defmodule Recursion do
  def print_multiple_times(msg, n) when n <= 1 do
    IO.puts msg
  end

  def print_multiple_times(msg, n) do
    IO.puts msg
    print_multiple_times(msg, n - 1)
  end
end

Recursion.print_multiple_times("Hello!", 3)
# Hello!
# Hello!
# Hello!

REPETIÇÃO

O PODER DA RECURSÃO

defmodule Math do
  def sum_list([head | tail], accumulator) do
    sum_list(tail, head + accumulator)
  end

  def sum_list([], accumulator) do
    accumulator
  end
end

IO.puts Math.sum_list([1, 2, 3], 0) #=> 6

REPETIÇÃO

REDUCE E MAP

Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)

# output
6

Enum.map([1, 2, 3], fn(x) -> x * 2 end)

# output
[2, 4, 6]

AFINAL, É BOM?

RESTRIÇÕES DE IDENTIFICADORES

AS VARIÁVEIS EM ELIXIR DEVEM COMEÇAR COM LETRA MINÚSCULA OU UM UNDERSCORE (_), TEREM UM OU MAIS CARACTERES, SENDO ELES LETRAS, NÚMEROS OU UNDERSCORE (_). NÃO PODEM SER KEYWORDS

CASE SENSITIVE

AS VARIÁVEIS EM ELIXIR SÃO CASE SENSITIVE. LOGO, someVar, somevar E SOMEVAR SÃO VARIÁVEIS DIFERENTES

TIPAGEM

ELIXIR É UMA LINGUAGEM DINAMICAMENTE TIPADA, LOGO, NÃO PRECISAMOS DEFINIR O TIPO DAS VÁRIAVEIS QUANDO INICIADAS, SEU TIPO É ATRIBUÍDO EM TEMPO DE EXECUÇÃO. ELA TAMBÉM É FORTEMENTE TIPADA.

PATTERN MATCHING

MATCH OPERATOR

# Assign value
x = 1
IO.puts x # 1

# Match operator
1 = x
# 1
2 = x
** (MatchError) no match of right hand side value: 1

PATTERN MATCHING

MATCH OPERATOR

{a, b, c} = {:hello, "world", 42}
# {:hello, "world", 42}

[a, b, c] = [1, 2, 3]
# [1, 2, 3]

[head | tail] = [1, 2, 3]
# [1, 2, 3]
head
# 1
tail
# [2, 3]

MUTABILIDADE

OS TIPOS DE DADOS EM ELIXIR SÃO IMUTÁVEIS. ISSO SIGNIFICA QUE, MODIFICANDO UMA VARIÁVEL, O ELIXIR RETORNA UMA NOVA ESTRUTURA. SENDO IMUTÁVEL, VOCÊ NÃO PRECISA SE PREOCUPAR QUE UM BLOCO DE CÓDIGO PARTICULAR ESTÁ MUTANDO SUA ESTRUTURA DE DADOS EM ALGUM LUGAR.

FUNÇÕES ANINHADAS

O ELIXIR NÃO SUPORTA FUNÇÕES ANINHADAS. PELAS SUAS REGRAS DE ESCOPO, PODEMOS ANINHAR MÓDULOS, E PARA CADA MÓDULO ANINHADO, DEFINIR FUNÇÕES.

FUNÇÕES ANINHADAS

defmodule P do
  defmodule Q do
    def q(false), do: "sorry"
    def q(true) do
      defmodule M do
        def say, do: "hi"
      end
    end
  end
end

P.Q.q false   #=> "sorry"

# the module hasn't been defined yet
P.Q.M.say     #=> undefined function: P.Q.M.say/0

# after this call the P.Q.M module will become available
P.Q.q true
P.Q.M.say     #=> "hi"

CIDADÃOS DE PRIMEIRA CLASSE

EM ELIXIR, AS FUNÇÕES ANÔNIMAS SÃO AS FUNÇÕES COMO CIDADÃOS DE PRIMEIRA CLASSE

defmodule Math do
  def square(x) do
    x * x
  end
end

Enum.map [1, 2, 3], &Math.square/1
#=> [1, 4, 9]

CIDADÃOS DE PRIMEIRA CLASSE

CLOSURES

closures = []

i = 0
closures = closures ++ [fn -> IO.puts i end]

i = 1
closures = closures ++ [fn -> IO.puts i end]

i = 2
closures = closures ++ [fn -> IO.puts i end]

Enum.at(closures, 0).()
Enum.at(closures, 1).()
Enum.at(closures, 2).()

# 0
# 1
# 2

GERENCIAMENTO DE DEPENDÊNCIAS

MIX

MIX

$ mix new example

* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/example.ex
* creating test
* creating test/test_helper.exs
* creating test/example_test.exs

MIX

defmodule Example.Mixfile do
  use Mix.Project

  def project do
    [app: :example,
     version: "0.0.1",
     elixir: "~> 1.0",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps]
  end

  def application do
    [applications: [:logger]]
  end

  defp deps do
    []
  end
end

MIX

def deps do
  [{:phoenix, "~> 0.16"},
   {:phoenix_html, "~> 2.1"},
   {:cowboy, "~> 1.0", only: [:dev, :test]},
   {:slim_fast, ">= 0.6.0"}]
end
$ mix deps.get

EM ELIXIR, TODO O CÓDIGO RODA EM PROCESSOS. PROCESSOS SÃO ISOLADOS UM DOS OUTROS, SÃO CONCORRENTES E SE COMUNICAM POR MENSAGENS.

CONCORRÊNCIA

OS PROCESSOS DO ELIXIR NÃO DEVEM SER CONFUNDIDOS COM OS PROCESSOS DO SISTEMA OPERACIONAL. ELES SÃO BEM LEVES EM TERMOS DE MEMÓRIA E CPU. POR CAUSA DISSO, NÃO É INCOMUM TER DEZENAS OU ATÉ CENTENAS DE MILHARES DE PROCESSOS RODANDO SIMULTANEAMENTE

CONCORRÊNCIA

parent = self()
#PID<0.41.0>

spawn fn -> send(parent, {:hello, self()}) end
#PID<0.48.0>

receive do
    {:hello, pid} -> "Got hello from #{inspect pid}"
end

# output
"Got hello from #PID<0.48.0>"

ALGUÉM REALMENTE TEM DÚVIDAS?

Elixir

By cristianossd

Elixir

Describing elixir language

  • 757