Los errores ocurren. En Elixir, aunque se suele decir «let it crash», hay ocasiones en las que necesitamos rescatar la llamada a la función hasta un estado válido conocido para cumplir con un contrato de software. En algunos lenguajes, los errores se usan como método de control de flujo, pero en Elixir este patrón no se recomienda. A menudo podemos reconocer las funciones que pueden lanzar un error solo por su nombre: las funciones que lanzan errores llevan ! al final de su nombre. Esto es en comparación con las funciones que devuelven {:ok, value} o :error. Mira estos ejemplos de la biblioteca:
Map.fetch(%{a: 1}, :b)
# => :error
Map.fetch!(%{a: 1}, :b)
# => raises KeyError
Elixir proporciona una construcción para rescatar errores usando try .. rescue
try do
raise RuntimeError, "error"
rescue
e in RuntimeError -> :error
end
Veamos esta construcción:
try.raise/2.rescue, hacemos coincidencia de patrones con el nombre del módulo del error lanzado
->:
e coincide con el struct del error.in es una palabra clave.RuntimeError es el error del que queremos rescatar.
_ en lugar del nombre del módulo u omitir por completo la palabra clave in.Los errores (a veces también llamados «excepciones») que rescatas de esta forma son structs. En Elixir, rara vez se rescatan errores. Por lo general, el error rescatado se registra o se envía a un servicio externo de monitoreo, y luego se vuelve a lanzar. Esto significa que normalmente no nos importa la estructura interna del struct de error específico.
En el concepto Excepciones aprenderás más sobre los structs de error, incluido cómo definir tu propio error personalizado.
Mientras trabajas en Instruments of Texas, se te encarga trabajar en una calculadora experimental de notación polaca inversa [RPN] escrita en Elixir. Tu equipo tiene un problema con algunas operaciones que lanzan errores y hacen que el proceso se caiga. Se te ha encargado escribir una función que envuelva a la función de operación para que los errores se puedan manejar de forma más elegante con código idiomático de Elixir.
Implementa la función calculate!/2 para llamar a la función de operación con la pila como único argumento. La función de operación está definida en otro lugar, pero sabes que puede completarse con éxito o lanzar un error.
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
Los nombres de funciones que terminan en
!son una advertencia para los programadores de que esta función puede lanzar un error
Cuando investigas más, te das cuenta de que muchas funciones usan átomos y tuplas para indicar su éxito o fracaso. Implementa calculate/2 usando esta estrategia.
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
Algunos de los errores contienen información importante que tus compañeros de trabajo necesitan para asegurar el correcto funcionamiento del sistema. Implementa calculate_verbose/2 para transmitir el mensaje de error. El error es una estructura que tiene un 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"}
Regístrate en Exercism para aprender y dominar Elixir con 58 conceptos168 ejercicios y mentoría humana real, todo gratis.