5 ms·
That’s an impressive amount of work to carry an error state around, and check before each step if there is an error set. I feel like this is a trivially simple
by zaroth 7y ago
That’s an impressive amount of work to carry an error state around, and check before each step if there is an error set.
I feel like this is a trivially simple idea which has been contorted by poor nomenclature and lacking the proper syntactic sugar, which results in significantly more cognitive load than should be necessary to achieve the desired result.
Kind of like that sentence actually.
- ryanmarsh 7y agoThere’s all manner of creative ways to solve problems in code but if they aren’t idiomatic then, IMHO, they aren’t applicable. Code should be first and foremost understandable by humans and only incidentally for compilers.
- nine_k 7y agoThere's a point where long boilerplate becomes unmaintainable by humans. Approaches like this remove boilerplate in intuitive ways; look at the amount of code in the `Left` and `Right` implementations. They make code more understandable. C needs not to be the idiom for Javascript, despite syntactic borrowings made in 1994.
- marmada 7y agoThis approach doesn't remove boilerplate, the initial version is 8 lines, the final version is 6 lines. Except the final version has a lot of code hidden in helper methods, making it impossible to understand at first glance without reading those helper methods. (LiftA2, chain, Left, Right, Lodash helper methods). The initial version is also very clear, we have a set of method calls that execute sequentially, and if any of them fail, we go to the catch block and call showError. This indicates that all functions in the try block can throw an error. The final version goes through a lot more trouble yet I don't get any extra information by reading the code. Now I don't even know which functions are error prone because everything is a mix of weird functions like map, chain, and liftA2. The second version also seems to promote more cognitive load. Try-Catch is simple, it doesn't require any brain cells to understand. This approach required an entire blog post. It seems better to dedicate mental energy to more important decisions rather than a simple function that processes CSVs. I think I understand the benefits of functional programming when one is writing a compiler, but this seems to be a misuse of functional techniques.
- nine_k 7y agoThe article's example is a toy example. In a real-world example of the same approach, like this piece of highly non-idiomatic Python that still reads much like plain English [1]. > Try-Catch is simple, it doesn't require any brain cells to understand ...because you have already spent the effort to learn it. Equally, monadic code can be quite compact and simple, once you have have spent the effort to learn a new approach. Also, a catch clause does not give you an idea what data cause the crash, only what line. The Either-based approach allows every place to report all relevant local info; the linked code has complete error reporting. Everything is non-idiomatic until it is. If you write JS and use Promises and `await`, you already use monadic behavior and an analog of Haskell's "do notation". Some might think that callbacks are more intuitive, though. [1]: https://pastebin.com/wY8ZC0mP https://pastebin.com/wY8ZC0mP
- munchbunny 7y agoI think this is something where it works well with language support (like Rust), but it seems really unidiomatic in JavaScript, and so it ends up hurting readability more than helping it. In a team context you would have to make a code style decision to handle things this way.
- ehnto 7y agoYou would then have to teach every developer who you onboard this novel way of doing things. I feel idiomatic code is really the best code, even if it's not always the most elegant way to achieve something it excels in practical ways. This is definitely one of the benefits of using a framework. Not every language has strong idioms for all uses, but you can often find them in a framework.
- nine_k 7y agoThere's no single definition of "best"; your is implicit, but what specifically do you imply? Straightforward, idiomatic imperative code can be "best" if you have to maintain a legacy app and can hire developers with little experience and desire to learn. Code which is less like Basic can be "best" if it allows your team to move twice as fast, and have 10x as short a backlog of defects to fix. It may take a more expensive team, and some onboarding time for new members, of course.
- acdha 7y ago> Code which is less like Basic can be "best" if it allows your team to move twice as fast, and have 10x as short a backlog of defects to fix. It may take a more expensive team, and some onboarding time for new members, of course. This is true in theory but it’s famously hard to measure, having both very strong religious beliefs and notorious confounds. For example, is any particular anecdote telling you that a niche language like Lisp or Haskell actually makes people more productive or just that you’re filtering for developers who would be above average in any language? Actually measuring these sorts of claims scientifically is expensive enough that it’s rarely attempted, but that rarely causes people to taper the degree of certainty people express. That doesn’t mean that new languages are bad but I think Fred Brooks was right to recommend skepticism of big claims rather than more incremental improvements.
- steego 7y agoYou're very polite. I'm going to be more frank. Monads like this are very problematic in the JavaScript world. I say this as an F# programmer, and our community is not unfamiliar with Railway Oriented Programming (https://fsharpforfunandprofit.com/rop/ https://fsharpforfunandprofit.com/rop/) Unlike F# or any other decent functional language, JavaScript doesn't have very good constructs to guide and ensure the programmer is using these constructs properly, let alone using alongside with existing error propagating constructs. What happens when you mix these with promises, or RxJs, or something else? My advice would to be avoid using constructs like this for general purpose error handling and use tried and true constructs for Exceptions, which should be exceptional. Something like this isn't entirely useless, sometimes its useful to encode errors in an abstract type when the errors become an integral part of the problem domain. Sometimes errors aren't exceptional and you need a way to deal with them that's somewhat performant. Even if that's the case, you need to be careful with constructs like this because it's easy to use them wrong, which might lead to scenarios where errors aren't properly thrown and propagated. I'm not saying to never use them. I'm just heeding you should tread carefully.
- nine_k 7y agoExceptions are for exceptional stuff, that is, something you do not normally expect. Their purpose is cleanup after a non-catastrophic failure. OTOH you totally expect I/O errors and parsing errors while reading a CSV file. You totally expect a mismatch when matching a regexp. Using exceptions here is discouraged even by Martin Fowler, of all luminaries [1]. [1]: https://martinfowler.com/articles/replaceThrowWithNotification.html https://martinfowler.com/articles/replaceThrowWithNotificati...
- mLuby 7y agoHuh, I thought it was the opposite: >we use the term exception for expected but irregular situations at runtime and the term error for mistakes in the running program that can be resolved only by fixing the program. https://wiki.haskell.org/Error_vs._Exception https://wiki.haskell.org/Error_vs._Exception
- 7y ago