Exception protocol

70 views
Skip to first unread message

Michał Muskała

unread,
Apr 3, 2017, 10:39:53 PM4/3/17
to elixir-l...@googlegroups.com
Hello everybody,

I was working recently a lot with errors and exceptions, and got annoyed by the very complex way an exception works. There are so many ways to use them, it gets very confusing.

First there is a struct with the magic __exception__ field. The associated module may or may not export both exception/1 and message/1 functions for different circumstances. It's not entirely clear to me when each of them is called.

The purpose of all of this is clear - allow different data structures to be used for exceptions. But the thing is - we already have a way to achieve this kind of polymorphism - protocols.

I propose adopting a protocol for formatting exceptions. For backwards compatibility we could keep the Any implementation call the associated module's message/1 function.

Let me also briefly explain the use case that lead me to this proposal: I'm working on a low-level library to replace (and make more flexible) the ecto type conversions and changeset validations (loosely inspired by Ruby's "dry" family of libraries). In case of errors, the library would return structs that could be used in two ways - one to provide programmer-friendly output as an exception, and the other to provide end-user-friendly output as validation errors. 

Regards,
Michał.

José Valim

unread,
Apr 3, 2017, 11:20:20 PM4/3/17
to elixir-l...@googlegroups.com
There are different concerns here:

* the __exception__ struct field is necessary for pattern matching on try/catch

* exception is called by raise. It is not for data structure polymorphism by a direct module to struct conversion

* message is always necessary by any exception module as it returns the message

If I understand your proposal, you want to replace the second item by a protocol. But the first bullet means that the protocol will still have to return an exception struct.

Can you please confirm what the protocol would replace and what the protocol functions should return?


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/d3986437-390f-437d-b742-599bb82b8f69%40Spark.
For more options, visit https://groups.google.com/d/optout.
--


José Valim
Skype: jv.ptec
Founder and Director of R&D

José Valim

unread,
Apr 3, 2017, 11:27:04 PM4/3/17
to elixir-l...@googlegroups.com
Btw, if I understand your concerns correctly, libraries tackle this by returning exception structs. See DBConnection for an example.

OvermindDL1

unread,
Apr 4, 2017, 10:45:57 AM4/4/17
to elixir-lang-core, jose....@plataformatec.com.br
I am a *huge* fan of returning exception structs instead of raising them, a few libraries follow this style as well like `exceptional` on hex.pm.

However, if you are wanting to format it, look at the Exception.message function, it takes an exception and formats it properly by looking at a message field on the struct or calling the modules message/1(?) function or whatever.

Robert Virding

unread,
Apr 5, 2017, 8:03:16 AM4/5/17
to elixir-lang-core, jose....@plataformatec.com.br
How do you mean "returning exception structs"? Isn't an exception something which is raised?

Robert

OvermindDL1

unread,
Apr 5, 2017, 10:55:08 AM4/5/17
to elixir-lang-core
A growing number of libraries are returning exception structs to report errors so you can aggregate them more easily (easily testable via the Elixir built-in `Exception.is_exception?/1`) since the error tuples would start to get large and unwieldy when holding a lot of data, and since you can pack a lot in to exception structs it is becoming more common.  See the library of https://github.com/expede/exceptional for one example that has popped up to use that style, it allows operating on and over things that might be error tuples, exception structs, and a few other styles, so you can operate over a thing in a pipeline regardless of if the value has failed.  An example (you have the optional to override `|>` with the exceptional library though it does not recommend it, its `~>` is the monadic style `|>` by default, so you can replace `|>` with `~>` everywhere if you want:

```elixir
this_might_fail() # Say this returns a success tuple of {:ok, blah} or {:error, blorp}
~> this_might_also_fail() # This could return a single successful value, or an exception structure
~> this_always_works() # This just mutates the value
~> blorp() # This returns a success or failure tuple like the first call above
~> bloop() # This just transforms the value but otherwise works fine
```
Thus without needing to do a huge amount of case statements (though nowadays the `with` construct could do this specific example, though `with` cannot do other things that `exceptional` can do) you can pipe a value through a set of calls, and if any fail then it fails out with that error without performing the later calls.  Exceptional also has functions to normalize the value to a value|%Exception_struct{} or you can normalize it to a {:ok, value}|{:error, exception_message} or you can normalize is to a value or a raised exception as well, and a few other things.  You can also invert it and use an exceptional def to define functions that handle it properly without using `~>`, good for libraries.

Its an odd style yes, but it was designed for easy piping and readability in mind.  Personally I'd be a fan of using {:ok, success:any()}|{:error, messages:map()} or something like that everywhere, but there are so many styles (even of just error tuples I've seen many N-tuple sizes, many different kinds of errors, very irritating to keep reformatting each individually for error display and so forth, which is why I've been personally been moving over time to the style of returning a {:ok, value} 2-tuple for a success case always and a {:error, %SomeException{}} for the error case always, reason is that the exception contains all of the information you need to debug it as well as it is easily formatable (using Elixir's built-in `Exception.message/1`) to a text format for easy displaying to the user as well.  Though this specific style is not as popular as returning the raw value or the exception struct directly, which is the growing style among a few things.
Reply all
Reply to author
Forward
0 new messages