Powered by AppSignal & Oban Pro

Basics

basics/basics.livemd

Basics

Kernel

Access kernel

Para interagir com o Elixir usamos o comandoiex

Comandos basicos

  • Soma 5 + 5
  • Sub 5 - 2
  • Multi 5 * 2
  • Divisao 5 / 2, vai sempre retornar um float
    • Para divivir e ter o inteiro usamos o div(5 + 2)

Operadores logicos

  • Temos todos os operadores lógicos como em qualquer linguagem
    • Exemplo &&, ||

String

  • Declaramos com "string"
  • Concatenação com uma variavel

String

  • Declaramos com “string”
  • Concatenação com uma variavel
x = "world"
"Hello #{x}"

# OR

"hello" <> " " <> "world"
  • Modificação de strings
    • No elixir não existem objetos, então não é possivel fazer "String".lower()
    • No elixir existem os modulos, que são como namespaces que basicamente são conjuntos de funções
      • Podemos acessar apartir do Modulo String
  • No elixir temos a aridade / arity das funções, que podemos ver no modulo de Sting por exemplo.

image

  • Ver a documentação com h antes do comando
    • Exemplo: h String.slice

Exemplo de aplicação do modulo String

String.downcase("UPPERCASE WORD")

Exemplo de imutablididade

  • Repare que a variavel x não mudificou diretamente Example

Atoms

  • Constantes onde a valor da constante é o proprio nome
    • Exemplo da declaração :atom
  • O true e o false no Elixir são Atoms
    • Podemos verificar o tipo das variaveis com o is_
      • is_atom()
      • is_string()

Comparação

  • No Elixir o == funciona como na matematica, ou seja, tem mesmo de ser igual
"ola" = "ola"
"ola" = "popo"

Existe o conceito de Pattern Matching

  • Em Elixir, o operador = é na verdade o nosso operador match, comparável ao sinal de igualdade da matemática. Quando usado, a expressão inteira se torna uma equação e faz com que Elixir combine os valores do lado esquerdo com os valores do lado direito da expressão. Se a comparação for bem sucedida, o valor da equação é retornado. Se não, um erro é lançado.

Listas

  • No Elixir as listas são encadeadas, ou seja:
    • x = [1,2,3] = [1] -> [2] -> [3]
    • Não podemos aceder x[0]
  • Valores variaveis:
    • [1, 2, 3 , "string"]
    • [1, 2.3, "string"]
  • Soma de listas
[1, 2, 3] ++ [4, 5, 6]
  • Substração de listas
[1, 2, 3] -- [1, 3]
  • Primeiro elemento da lista
hd([1, 2, 3])
  • Cauda da lista -> todos os elementos menos o primeiro
tl([1, 2, 3])

Tuplas

  • Uma lista de chaves, como um objeto
x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

elem(x, 7)
  • Modificação de um elemento na tupla
x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

put_elem(x, 7, "banana")
  • Maior uso das tuplas
    • Em operações, onde a 1 posição é um Atom e o 2 é o retorno da função
    • ok -> sucesso
    • error-> error

Maps

  • Estrutura de dados chave: valor
    • %{a: 1, b: 2, c: 3} ou
    • %{"a" => 1, "b" => 2, "c" => 3}
%{a: 1, b: 2, c: 3}

Diferenças

  • Acesso as chaves
my_map = %{a: 1, b: 2, c: 3}
my_map.b
my_map = %{"a" => 1, "b" => 2, "c" => 3}
my_map["c"]
  • Diferentes tipos de dados
    • %{"a" => 1, "b" => "banana", "c" => 3.6}

Modulos auxiliares das listas

  • String
  • Map
  • List
my_map = %{"a" => 1, "b" => 2, "c" => 3}
Map.put(my_map, "d", "novo")

Enum

  • Provides a set of algorithms to work with enumerables.

    • Usege with Lists, Maps and Rages
  • Reduce

    • Invokes fun for each element in the enumerable with the

accumulator.

  • The initial value of the accumulator is acc. The function is invoked for each element in the enumerable with the accumulator. The result returned by the function is used as the accumulator for the next iteration. The function returns the last accumulator.
Enum.reduce([1, 2, 3], 0, fn x, acc -> x + acc end)

Case

  • Matches the given expression against the given clauses.
## Examples
file = "test.txt"

case File.read(file) do
  {:ok, contents} when is_binary(contents) ->
    String.split(contents, "\n")

  {:error, _reason} ->
    IO.inspect("could not find #{file}, assuming empty...")
end

In the example above, we match the result of File.read/1 against each clause “head” and execute the clause “body” corresponding to the first clause that matches.

If no clause matches, an error is raised. For this reason, it may be necessary to add a final catch-all clause (like _) which will always match.

x = 10

case x do
  0 ->
    "This clause won't match"

  _ ->
    "This clause would match any value (x = #{x})"
end
#=> "This clause would match any value (x = 10)"

Pipe operator

  • The pipe operator |> passes the result of an expression as the first parameter of another expression.
"Elixir rocks"
|> String.upcase()
|> String.split()

Programming can get messy. So messy in fact that function calls can get so embedded that they become difficult to follow. Take the following nested functions into consideration:

foo(bar(baz(new_function(other_function()))))

Here, we are passing the valueother_function/0 to new_function/1, and new_function/1 to baz/1, baz/1 to bar/1, and finally the result of bar/1 to foo/1. Elixir takes a pragmatic approach to this syntactical chaos by giving us the pipe operator. The pipe operator which looks like |> takes the result of one expression, and passes it on. Let’s take another look at the code snippet above rewritten using the pipe operator.

other_function() |> new_function() |> baz() |> bar() |> foo()

Functions

  • Anounimous function

We can declare as

Enum.map(fn line -> parse_line(line) end)

or

Enum.map(&parse_line(&1))

In this case the first one is more explicit becasuse we are receiving an element

But when is not the case we can use the second onde notation

List.update_at(2, &String.to_integer/1)

Guards

Guards are a way to augment pattern matching with more complex checks. They are allowed in a predefined set of constructs where pattern matching is allowed.

Not all expressions are allowed in guard clauses, but only a handful of them. This is a deliberate choice. This way, Elixir (and Erlang) can make sure that nothing bad happens while executing guards and no mutations happen anywhere. It also allows the compiler to optimize the code related to guards efficiently.

def empty_map?(map) when map_size(map) == 0, do: true
def empty_map?(map) when is_map(map), do: false