1.4.0
A long-requested feature. I'm finally caving and writing it π
normalize(42)
#=> 42
normalize(%Enum.OutOfBoundsError{message: "out of bounds error"})
#=> %Enum.OutOfBoundsError{message: "out of bounds error"}
normalize(:error)
#=> %ErlangError{original: nil}
normalize({:error, "boom"})
#=> %ErlangError{original: "boom"}
normalize({:error, {1, 2, 3}})
#=> %ErlangError{original: {1, 2, 3}}
normalize({:error, "boom with stacktrace", ["trace"]})
#=> %ErlangError{original: "boom with stacktrace"}
normalize({:good, "tuple", ["value"]})
#=> {:good, "tuple", ["value"]}
Something similar was suggested back in #2. I still believe that you should pattern match as much as possible, making normalize
superfluous... but this pipes well enough in most situations.
This isn't a complete solution for all tagged tuples. In fact, it is emphatically not that. For example:
normalize({:my_special, "tuple", ["value"]})
#=> {:my_special, "tuple", ["value"]}
βοΈ You'll still need to handle those cases in pattern matches yourself (unless you do want the tuple value, which is a common case). To do this, you may pass an additional converting function. Example:
{:oh_no, {"something bad happened", %{bad: :thing}}}
|> normalize(fn
{:oh_no, {message, _}} -> %File.Error{reason: message}) # This case
{:bang, message -> %File.CopyError{reason: message})
otherwise -> otherwise
end)
#=> %File.Error{message: msg}
{:oh_yes, {1, 2, 3}}
|> normalize(fn
{:oh_no, {message, _}} -> %File.Error{reason: message})
{:bang, message -> %File.CopyError{reason: message})
otherwise -> otherwise # This case
end)
#=> {:oh_yes, {1, 2, 3}}
The advantage for all of the common patterns is that we have a single struct representation for pattern matching (unless the error tuple is not one of the most common cases). Hopefully including this function doesn't cause any confusion π¨ Time will tell...