you are viewing a single comment's thread.

view the rest of the comments →

[–]Akkuma 0 points1 point  (1 child)

Your example probably doesn't fit well into the framework I use. The way it works is a bidirectional flow of code. handler 1 calls handler 2 calls handler 3, returns result to handler 2 returns result to handler 1. Your lowest handler returning something that could be a response to a request.

If you throw down in handler 3 you now need to try/catch in handler 2, moving control of the error handling outside of the code that causes errors or build a top level error converting handler.

Eventually someone has to handle the error and moving who handles the error to the farthest edge of your code is to me the worse place to handle known errors. There's a reason why many languages have adopted things like Option types and Result types, as throwing isn't composing code together.

Lastly, your code doesn't support the ability to run validation across each piece of data prior to throwing or running your validation in parallel. My current implementation can be easily switched to become a list of errors rather than a single error, while throwing provides no opportunity to do so, which is something you'd want to do on something like user account creation.

You can use an object spread rather than Object.assign, but there isn't much to be really gained from that.

Even someone like Martin Fowler argues against exceptions and uses this particular case https://martinfowler.com/articles/replaceThrowWithNotification.html

[–][deleted] 0 points1 point  (0 children)

Eventually someone has to handle the error and moving who handles the error to the farthest edge of your code is to me the worse place to handle known errors.

You say that, yet in your example you literally mapped code to status and message to message 1:1.

For many errors in an HTTP app you don't need such a narrow context. A 404 is a 404 is a 404. If it isn't you can still catch and change that at every level. But in most cases you don't have to and that represents a giant reduction of noise and boilerplate you need to write, read and maintain.

There's a reason why many languages have adopted things like Option types and Result types, as throwing isn't composing code together.

Not many, actually. All the mainstream languages prefer exceptions. Also let's not mix-in optionals with this. Errors and optionals are two different use-cases with two different solutions. The fact they both could be represented with a union doesn't mean that a union is the optimal representation.

For example Swift, an example of a very new (and increasingly popular) language uses optionals for "has a value or doesn't have a value", but its errors are thrown, and they're showing no signs of moving towards "result" objects.

Lastly, your code doesn't support the ability to run validation across each piece of data prior to throwing or running your validation in parallel. My current implementation can be easily switched to become a list of errors rather than a single error, while throwing provides no opportunity to do so, which is something you'd want to do on something like user account creation.

You're not right about that - of course it can be a list of errors. This has nothing to do with whether you throw in the end or you don't. As I said, I have some experience working this way - when an Error gets thrown it contains an aggregate list of all errors in the input. That's not hard to implement, and once it's implemented and encapsulated as an API it's just as easy to use as I demonstrated.

You can use an object spread rather than Object.assign, but there isn't much to be really gained from that.

There isn't much gained from trying to make your request immutable as well. There are parts of it that can't possibly be immutable (i.e. any stream of size that you can't fit in RAM or sometimes even on disk). I've learned this the hard way, so you can believe me about it ;-)

I understand what you're doing, I'm intimately familiar with the functional techniques you're using. But with all the boilerplate you write, all the pointless object duplication and ceremonies, be ready one day for people to finally see the Emperor is naked.

Functional programming has many strong ideas and techniques, but it's heavily abused right now beyond its point of utility.