defmodule Demo do
defmacro maybe(do: block) do
quote do
try do
unquote(block)
rescue
e in [MatchError] -> e.term
# %MatchError{term: term} -> term
e -> reraise e, __STACKTRACE__
end
end
end
end
Why in the rescue it just match the name of the error?
In another words, why can not use the code like %MatchError{term: term} -> term in here?
About 5 years ago there is a question about this.
The different is that I know what is right syntax, but I do not know why it must like that way.
Briefly summarized, the reason is indeed to make it very hard for people to depend in their rescue clauses on the internals of the exception struct, as this might change over time. (Side note: debugging exceptions being raised because of other exceptions not being recognized properly is very much not fun.)
You can still do it inside the rescue clause if you really really want to, but at that point youâll have thought really hard about if this really is the approach you want to take and hopefully have reconsidered.
Now I have some more interesting things about rescue and catch.
%MatchError{} = try do
raise MatchError, term: {:ok, 1}
catch
:error, %MatchError{} = v -> v
end
%MatchError{} = try do
raise MatchError, term: {:ok, 1}
rescue
v -> v
end
If the error is raised by ourself, we can catch it and rescue from it. That is good.
But when runtime raise it, like this:
try do
:ok = :error
catch
# not work
# :error, %MatchError{}=v -> v
:error, v -> v # now v is not a Error, but a tuple
end
%MatchError{} = try do
:ok = :error
rescue
v -> v # now v is a MatchError
end
We can still rescue from the error, but when we try to catch it, we get a different thing(a tuple).
That is like Schrodingerâs cat.
A new mechanism for this was introduced in OTP 24 (c.f. EEP 54). The âextra informationâ is kept separate from the main value that is being raised, to make sure that existing code did not break.
It was necessary to do it this way in part because the whole error mechanism in Erlang predates maps and was thus solely built on top of atoms and tuples.