3 ms·
Your example is too simple. Yes, if what you want is the exact same semantics that you get with exceptions, Either is little better than exceptions. I believe
by mechanical_fish 15y ago
Your example is too simple. Yes, if what you want is the exact same semantics that you get with exceptions, Either is little better than exceptions.
I believe the trick with Either is that it assists one in designing completely different strategies. For example, you can design plugins that accept ambiguous arguments. Sometimes -- often, actually -- you really want to write this:
user_photo = load_user_photo(username)
ui.display_user_page(username, user_photo)
but instead you are compelled to write something like this:
try {
user_photo = load_user_photo(username)
} catch (PhotoFormatException e) {
ui.tell_the_user_the_format_is_bad
user_photo = NULL
} catch (Exception e) {
if (ui.interactive?) {
ui.tell_the_user_a_terrible_error_occurred(e)
}
unless (ui.app_in_production?) {
throw new MyScopeException(e, "bug")
}
}
if (is_null(user_photo)) {
ui.display_user_page_without_photo(username)
}
else {
ui.display_user_page_with_photo(username, user_photo)
}
(Now, I wrote that in an extra-clumsy way, but only so that we might fully appreciate the clumsiness of the logic. In the real world, I'd probably try to do this more elegantly. The result would probably (a) look cleaner; (b) be far more abstract, confusing, and overdesigned than it needed to be; and (c) contain a half-implemented broken implementation of the Either pattern. ;)
What we have here is an application that might need to behave differently if there is a photo or not, and differently if the user is a production customer, a script running in production, or a developer running on a local machine. And because exceptions behave like exceptions whether I like it or not -- they always travel up the stack, they're always in a panic -- that try/catch logic has to be here, at this level.
But if load_user_photo() could return an Either I could just pass that Either to ui.display_user_page(). And then I could plug in a "ui" class that does something like this [1]:
class Ui {
function display_user_page(username, user_photo) {
# The customer is blind; user_photo is irrelevant.
say("The username is " + username)
}
}
Or this:
class Ui {
function display_user_page(username, user_photo) {
print("The username is " + username)
if user_photo.is_good?() {
display_photo(user_photo)
}
}
}
Or this:
class Ui {
function display_user_page(username, user_photo) {
if user_photo.is_bad?() {
throw new HorrorException("I am shocked")
}
}
}
or whatever you like. This logic doesn't necessarily belong in your main logic, it belongs in a plugin, and the Either pattern helps you send it there.
---
[1] These are oversimplified examples too, of course. In the real world my class would probably do a little more exception handling. There is a certain amount of irreducible pain in dealing with the infinite number of things that might go wrong; all the language can do is give us the flexibility to organize the resulting pile of code as best we can.
- kstenerud 15y agoSo basically an Either is for returning a different (ostensibly error) type in a language that normally cares about the declared return type in a method signature? How would that work for passing the result to another method for analysis? Relax the rules there too? Wouldn't that require doing away with compiler/interpretor type enforcement altogether? (not that I have a problem with that)
- prodigal_erik 15y agoThe type checker will confirm that other function accepts a (Either Bad Good) as its argument. If you want to use a function that only takes a Good and returns a SomethingElse, call it in a "do" block and the call will be skipped if you got a Bad (because in the Either monad, the Left value is used for short-circuit failure) or use liftM to convert it to a function that takes a (Either Bad Good) and returns a (Either Bad SomethingElse). A lot of other languages had to bake in exception type checking as a weird special case, because even though the type of a function (because that's really about when you can and cannot use it) should depend on which exceptions it handles, they made that way too much hassle to actually enforce. However, Haskell is still evolving, and people have also added a couple versions of the typical bypass-the-type-system exception trainwreck, for reasons that escape me. http://www.randomhacks.net/articles/2007/03/10/haskell-8-ways-to-report-errors http://www.randomhacks.net/articles/2007/03/10/haskell-8-way...