在Erlang中,当引发错误时,我可以通过向error
:传递第二个参数来将参数包括到当前函数中
-module(foo).
-export([foo/2]).
foo(A, B) ->
error(something_went_wrong, [A, B]).
这意味着参数将在堆栈跟踪中可见,这可以使调试更容易:
> foo:foo(1, 2).
foo:foo(1, 2).
** exception error: something_went_wrong
in function foo:foo/2
called as foo:foo(1,2)
在Erlang和Elixir中,函数子句错误时都会自动发生这种情况:
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
除了直接调用:erlang.error
之外,是否有一种方法可以在Elixir中为显式引发的异常包含函数参数?我查看了Kernel.raise
的文档,没有发现任何合适的内容。
这感觉像是一种逃避,但我认为最好的方法是在消息中的变量上使用inspect/2
,例如
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"
Elixir defexception文档中的示例或多或少都是这样做的,只是附加到一个自定义异常模块,类似于:
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"
有了Erlang的背景,您可能能够更清楚地了解所有这些是如何通过异常模块返回的
没有通用的方法可以做到这一点,因为elixir中的异常只是一个结构。另一方面,这为您提供了灵活性,例如,在前面提到的FunctionClauseError
中使用的灵活性,即
defexception [:module, :function, :arity, :kind, :args, :clauses]
因此,当编译器提出它时,message
被构造为包括与原始错误一起传递的参数。
为了实现这一点,通常使用Kernel.defexception/1
定义自定义异常,接受参数并从Exception
行为实现message/1
和blame/2
回调。一个很好的例子是FunctionClauseError
实现的来源,我在上面链接了它。