Percursos
/
Elixir
Elixir
/
Exercícios
/
Calculadora RPN
Calculadora RPN

Calculadora RPN

Exercício de aprendizagem

Introdução

Erros

Os erros acontecem. Em Elixir, embora se diga muitas vezes para "let it crash", há alturas em que precisamos de resgatar a chamada à função para um estado bom conhecido, a fim de cumprir um contrato de software. Nalgumas linguagens, os erros são usados como método de controlo de fluxo, mas em Elixir este padrão é desaconselhado. Muitas vezes, conseguimos reconhecer funções que podem lançar um erro apenas pelo nome: as funções que lançam erros têm de ter ! no fim do nome. Isto, em comparação com funções que devolvem {:ok, value} ou :error. Repara nestes exemplos da biblioteca:

Map.fetch(%{a: 1}, :b)
# => :error
Map.fetch!(%{a: 1}, :b)
# => raises KeyError

Try/Rescue

O Elixir disponibiliza uma construção para resgatar de erros usando try .. rescue

try do
  raise RuntimeError, "error"
rescue
  e in RuntimeError -> :error
end

Vamos analisar esta construção:

  • Linha 1, o bloco é declarado com try.
  • Linha 2, a chamada à função que pode gerar um erro é colocada aqui; neste caso, estamos a chamar raise/2.
  • Linha 4, na secção rescue, fazemos correspondência de padrões com o nome do módulo do erro lançado
    • no lado esquerdo de ->:
      • e corresponde à struct de erro.
      • in é uma palavra-chave.
      • RuntimeError é o erro que queremos resgatar.
        • Se quiséssemos resgatar todos os erros, podíamos usar _ em vez do nome do módulo ou omitir completamente a palavra-chave in.
    • no lado direito:
      • as instruções a executar se o erro corresponder.

Structs de erro

Os erros (às vezes também chamados "exceções") que resgatas desta forma são structs. Resgatar erros em Elixir é algo que se faz muito raramente. Normalmente, o erro resgatado é registado ou enviado para um serviço de monitorização externo, e depois é relançado. Isto significa que, normalmente, não nos preocupamos com a estrutura interna da struct de erro específica.

No conceito de Exceções vais aprender mais sobre structs de erro, incluindo como definir o teu próprio erro personalizado.

Instruções

Enquanto trabalhas na Instruments of Texas, é-te pedido que trabalhes numa calculadora experimental de Notação Polaca Inversa [RPN] escrita em Elixir. A tua equipa tem um problema: algumas operações lançam erros e derrubam o processo. Foi-te pedido que escrevas uma função que encapsule a função de operação, para que os erros possam ser tratados de forma mais elegante com código Elixir idiomático.

1. Avisa a equipa

Implementa a função calculate!/2 para chamar a função de operação com a pilha como único argumento. A função de operação está definida noutro local, mas sabes que pode ou terminar com sucesso ou lançar um erro.

stack = []
operation = fn _ -> :ok end
RPNCalculator.calculate!(stack, operation)
# => :ok

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate!(stack, operation)
# => ** (ArgumentError) An error occurred

Os nomes de funções que terminam em ! são um aviso aos programadores de que essa função pode lançar um erro

2. Encapsula o erro

Ao fazeres mais pesquisa, reparas que muitas funções usam átomos e tuplos para indicar o sucesso ou a falha. Implementa calculate/2 com esta estratégia.

stack = []
operation = fn _ -> "operation completed" end
RPNCalculator.calculate(stack, operation)
# => {:ok, "operation completed"}

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate(stack, operation)
# => :error

3. Transmite a mensagem

Alguns dos erros contêm informação importante que os teus colegas precisam de ter para garantir o correto funcionamento do sistema. Implementa calculate_verbose/2 para transmitir a mensagem de erro. O erro é uma struct que tem um campo :message.

stack = []
operation = fn _ -> "operation completed" end
RPNCalculator.calculate_verbose(stack, operation)
# => {:ok, "operation completed"}

stack = []
operation = fn _ -> raise ArgumentError, "An error occurred" end
RPNCalculator.calculate_verbose(stack, operation)
# => {:error, "An error occurred"}
Editar via GitHub A ligação abre numa nova janela ou separador
Elixir Exercism

Estás pronto para começar Calculadora RPN?

Inscreve-te no Exercism para aprenderes e dominares Elixir com 58 conceitos168 exercícios, e mentoria humana real, tudo grátis.