Casamento de padrões - Lago azul
PUBLISHED
FILE_ID: CASAMENTO DE PADRÕES - LAGO AZUL

Casamento de padrões - Lago azul

Willian Frantz
Jun 15, 2025
PerformanceOptimizationWeb VitalsMonitoring

Você sabia que tanto Erlang como Elixir não possuem um operador de atribuição? Ou seja, o que significa a seguinte expressão abaixo:

1message = "Hello World"

Mais conhecido como Pattern Matching, esse mecanismo te permite fazer associações de valores através da comparação de símbolos entre o lado esquerdo do operador e o lado direito.

Na prática:

1iex> hello_world = "Hello World" 2iex> hello_world 3> "Hello World" 4 5iex> "Hello " <> world = "Hello World" 6iex> world 7> "World"

Os 2 exemplos acima ilustram o funcionamento do casamento de padrões. Onde a primeira execução aparenta ser uma simples atribuição, mas a partir da segunda, é possível perceber uma grande diferença conceitual.

No segundo exemplo a runtime irá tentar encontrar o valor "Hello " no valor do lado direito do operador, se esse valor for encontrado(deu match) o resto do valor será armazenado na variável world.d

Quando essa assertiva não acontece, e o padrão não é encontrado, uma exception é gerada.

1iex> "Hellu " <> world = "Hello World" 2> ** (MatchError) no match of right hand side value: "Hello World"

Essa é uma das minhas funcionalidades favoritas no ecossistema Erlang/Elixir, e neste texto vou tentar sintetizar um pouco do porquê.

Ex-1. Em Elixir, o casamento de padrões pode ser utilizado em Strings:

1# Apenas uma comparação e nada acontece, pois o padrão é encontrado (caso contrário, geraria uma exception) 2"Hello World" = "Hello World" 3 4# Assimila o valor "World" à variável world 5"Hello " <> world = "Hello World" 6 7# Mesmo resultado do exemplo acima, porém escrito de uma forma diferente. (usando operadores de bitstring) 8<<"Hello ", world::binary>> = "Hello World" 9 10# Assimila os primeiros 5 caracteres na variável hello, valida um espaço " ", e joga o resto da string na variável world 11<<hello::binary-size(5), " ", world::binary>> = "Hello World" 12 13# Como não existe nenhum padrão a ser encontrado, meio que simula uma atribuição. 14hello_world = "Hello World"

É possível usar casamento de padrões com praticamente todos tipos, strings, listas, tuplas, maps, structs...

Ex-2. Testando outros tipos:

1# Apenas procura o padrão do lado esquerdo no lado direito do operador, o mesmo com as strings. 2[1, 2, 3] = [1, 2, 3] 3 4# Assimila o valor 2 na variável second. 5[1, second, 3] = [1, 2, 3] 6 7# Assimila o valor 1 na variável first, e descarta os demais valores. (operadores importantes que usamos para listas `++` e `|`) 8[first | _] = [1, 2, 3] 9 10# Ignora apenas o primeiro valor da lista, e assimila os demais na variável rest. (2, 3) 11[_ | rest] = [1, 2, 3] 12 13# Simplesmente joga todos os valores da direita do operador na variável list. 14list = [1, 2, 3]

E o quão relevante é este tipo de operação no dia-a-dia? Casamento de padrões pode ser utilizado em praticamente tudo, validação, polimorfia de funções, estruturas de controle, funções de ordem maior.

Ex-3. Em Elixir costumamos trabalhar bastante com o conceito de Tuplas de erro ou sucesso, e isso vale para requests, comunicação externa, funções impuras, e por ai adiante...

Digamos que a execução de uma função qualquer do nosso código irá retornar uma tupla de sucesso, contendo {status, person} Podemos tirar vantagem do casamento de padrões nesse cenário para criar uma tomada de decisão no nosso código.

Ilustração:

1{:ok, %{name: "Willian"}} = {:ok, %{name: "Willian"}} 2 3# Assimila o valor "Willian" na variável name. 4{:ok, %{name: name}} = {:ok, %{name: "Willian"}} 5 6# Assimila todo o map da pessoa na variável person 7{:ok, person} = {:ok, %{name: "Willian"}} 8 9# Assimila :ok no status e o map pessoa na variável person 10{status, person} = {:ok, %{name: "Willian"}} 11 12# Joga todo o valor da direita do operador na variável tuple_result 13tuple_result = {:ok, %{name: "Willian"}}

Criando uma tomada de decisão a partir desse valor:

1defmodule Person do 2 def print_name(result) do 3 case result do 4 {:ok, %{name: name}} -> 5 IO.puts(name) 6 7 _ -> 8 IO.puts("Houve um erro ao processar o resultado") 9 end 10 end 11end 12 13# se executarmos este código passando nosso resultado do exemplo anterior teríamos algo como: 14iex> Person.print_name({:ok, %{name: "Willian"}}) 15> "Willian"

Agora com polimorfismo:

1defmodule Person do 2 def print_name({:ok, %{name: name}}), 3 do: IO.puts(name) 4 5 def print_name(_), 6 do: IO.puts("Houve um erro ao processar o resultado") 7end

Em funções de ordem maior:

1list_results = [ 2 {:ok, %{name: "Willian"}}, 3 {:ok, %{name: "Willian"}}, 4 {:error, "Algo deu ruim"}, 5 {:ok, %{name: "Willian"}}, 6 {:ok, %{name: "Willian"}}, 7 {:ok, %{name: "Willian"}}, 8 {:error, "Algo deu ruim"} 9] 10 11res = Enum.filter(list_results, fn 12 {:ok, %{name: name}} -> true 13 {:error, _reason} -> false 14end)

No caso acima, iremos filtrar da lista de resultados somente as tuplas de sucesso, e ignorar as que tiveram erro.

Concluindo, essa funcionalidade me permiti flexibilidade na hora de manipular dados e basicamente escrever toda estrutura do meu código, tratamento de erros, estruturas condicionais, polimorfismo, e muito mais que não consigo nem lembrar direito.

END_OF_FILE • ARTICLE_COMPLETE