However, it requires language level support to use easily - you need mutually exclusive types like discriminated unions. It's harder to do in most OO languages like C#/Java/Javascript, but you can do it with some effort. Which is why most don't.
However, it requires language level support to use easily - you need mutually exclusive types like discriminated unions. It's harder to do in most OO languages like C#/Java/Javascript, but you can do it with some effort. Which is why most don't.
The fundamental problem, IMHO, with error-handling is the call/return nature of most of our programming languages. That means you have to return something from you function/procedure. If you have nothing to return (error) or nothing to return yet (async), you have a problem, particularly with intermediate functions that don't really know what happened below them and don't really have enough of the context from their callers to handle the error.
Exceptions allow you to skip the these intermediate layers. ROP makes you handle all those layers, but gives you some tools to help make sure you did it correctly.
Filters, on the other hand, do not return results. Instead they actively pass their results on to the next filter in line. That means that in the case of an error, they can do what functions/procedures cannot: simply not produce a result at all. The next filter will be none-the-wiser.
You can then have the filter have an out-of-band mechanism for actually dealing with the error, analogous to Unix stderr.
I actually consider this a benefit, not a problem. The alternative, as I see it, is to side effect... which 99% of the time is a bad idea if there's an alternative.
> particularly with intermediate functions that don't really know what happened below them and don't really have enough of the context from their callers to handle the error
If you write a `map` function, you can use it in your pipeline to map over the Ok case and your intermediate functions don't need to know they're taking an Ok|Error type. This is exactly the same as what occurs in the `map`/`select` higher ordered function that's on the `list` type. The function passed to map/select doesn't know that it's being used on a list. In the same way, the function being passed to `Ok.map` doesn't know that it's being used on a discriminated union.
> You can then have the filter have an out-of-band mechanism for actually dealing with the error, analogous to Unix stderr.
To me this is a side effect and should be avoided. Basically your filter is partitioning your list into two lists, one of OKs and one of Errors, logging the errors, and then not reporting anything back to the consumer. LMK if I'm misunderstanding.
> side effect... which 99% of the time is a bad idea
Nope. A filter does not have "side" effects. It has effects, as in what you want to achieve. And it turns out that having an effect is a good idea about 100% of the time, because otherwise your program is 100% useless. Now when your primary architectural style is call/return based, effects of the program that are not encoded in the return value do become "side" effects, but that's a limitation of call/return (which FP turns up to 11), not a problem of effects in general.
> > You can then have the filter have an out-of-band mechanism
> To me this is a side effect and should be avoided.
Why? Apart from the religious mantra of "it is a side effect"?
When I adopted this pattern, it turned out to be hugely beneficial. It keeps your happy-path happy, no need to pollute it at all with error handling concerns. And it lets you customise and centralise your error handling, which turns out to be what you usually want, but without the call-stack shenanigans of exceptions.
ROP kinda sorta attempts to do something similar, but due to the constraints of C/R, it turns out to be far more complex.
[1] https://dl.acm.org/doi/10.1145/3397537.3397546
[2] https://www.youtube.com/watch?v=Gel8ffr4pqw
[3] https://2020.programming-conference.org/details/salon-2020-p...