Reputation: 41618
In Erlang, when raising an error I can include the arguments to the current function by passing a second argument to error
:
-module(foo).
-export([foo/2]).
foo(A, B) ->
error(something_went_wrong, [A, B]).
This means that the arguments will be visible in the stack trace, which can make debugging easier:
> foo:foo(1, 2).
foo:foo(1, 2).
** exception error: something_went_wrong
in function foo:foo/2
called as foo:foo(1,2)
In both Erlang and Elixir, this happens automatically on function clause errors:
defmodule Foo do
def foo(a, b) when false do
:ok
end
end
> Foo.foo(1, 2)
** (FunctionClauseError) no function clause matching in Foo.foo/2
The following arguments were given to Foo.foo/2:
# 1
1
# 2
2
foo.ex:2: Foo.foo/2
Is there a way to include function arguments for explicitly raised exceptions in Elixir, other than calling :erlang.error
directly? I looked at the documentation for Kernel.raise
and didn't find anything suitable.
Upvotes: 1
Views: 412
Reputation: 121010
There is no generic way of doing that because the exception in elixir is just a struct. On the other hand, that gives you the flexibility that is, for instance, used in the aforementioned FunctionClauseError
, that is defined as
defexception [:module, :function, :arity, :kind, :args, :clauses]
So when the compiler raises it, message
is constructed to include arguments passed through with the original error.
To achieve the same, one usually defines the custom exception with Kernel.defexception/1
, accepting arguments and implementing both message/1
and blame/2
callbacks from Exception
behaviour. A good example would be the source of FunctionClauseError
implementation, I linked above.
Upvotes: 1
Reputation: 9618
It feels like a cop-out, but I believe the preferred way of doing this is to use inspect/2
on variables in your message, e.g.
raise ArgumentError,
message: "There was a problem with the inputs: #{inspect(a)} #{inspect(b)}"
# Yields:
# ** (ArgumentError) There was a problem with the inputs: "foo" "bar"
The examples in the Elixir defexception docs more or less do the same, just attached to a custom exception module, something like:
defmodule ABError do
defexception [:message]
end
a = "foo"
b = "bar"
raise ABError,
message: "There was a problem with the inputs: #{inspect(a)} #{inspect(b)}"
# Yields:
# ** (ABError) There was a problem with the inputs: "foo" "bar"
With your background in Erlang, you might be able to make more sense of how all this threads its way back through the Exception module
Upvotes: 1