Favorite Anti-Pattern



  • @Carnage said in Favorite Anti-Pattern:

    @hungrier said in Favorite Anti-Pattern:

    @Carnage said in Favorite Anti-Pattern:

    I just use Kotlin, is there any good bits of lombok that would make it preferable over Kotlin?

    Lombok isn't its own language, but lets you annotate your Java classes to automagically generate get/setters, constructors, static logger, etc.

    Yeah, but from a quick glance, Kotlin seems to solve many of the same problems that lombok does.

    Maybe. I don't know anything about Kotlin, but I've used Lombok at work and it's saved me a bit of time whenever I remember to actually use it


  • Banned

    @Carnage said in Favorite Anti-Pattern:

    The last few years I haven't actually seen many anti-patterns. Not sure why.

    Are you a backend dev by any chance? 🚎


  • Considered Harmful

    @error said in Favorite Anti-Pattern:

    @hungrier said in Favorite Anti-Pattern:

    @Carnage said in Favorite Anti-Pattern:

    I just use Kotlin, is there any good bits of lombok that would make it preferable over Kotlin?

    Lombok isn't its own language, but lets you annotate your Java classes to automagically generate get/setters, constructors, static logger, etc.

    I'll probably start using that in the Minecraft mod.

    It doesn't seem to understand that I prefix private members with _. Toby faire, that's non very common in Java.

    Nevermind, RTFM. There's a config file.


  • Considered Harmful

    @error said in Favorite Anti-Pattern:

    @error said in Favorite Anti-Pattern:

    @hungrier said in Favorite Anti-Pattern:

    @Carnage said in Favorite Anti-Pattern:

    I just use Kotlin, is there any good bits of lombok that would make it preferable over Kotlin?

    Lombok isn't its own language, but lets you annotate your Java classes to automagically generate get/setters, constructors, static logger, etc.

    I'll probably start using that in the Minecraft mod.

    It doesn't seem to understand that I prefix private members with _. Toby faire, that's non very common in Java.

    Nevermind, RTFM. There's a config file.

    OK, this almost makes Java tolerable. Auto properties, val/var. Fuck yeah.


  • Notification Spam Recipient

    @error said in Favorite Anti-Pattern:

    Fuck yeah.

    Java! Fuck yeah!



  • @Gąska said in Favorite Anti-Pattern:

    @Carnage said in Favorite Anti-Pattern:

    The last few years I haven't actually seen many anti-patterns. Not sure why.

    Are you a backend dev by any chance? 🚎

    yeah...
    But the frontend part of the system I'm currently working on isn't even good enough to have anti--patterns. If anything, it'd be called chaotic bowl of sick,


  • Banned

    @Carnage see, back in the day, frontend and backend were one thing, and the good devs kept the bad devs somewhat under control...



  • @Gąska said in Favorite Anti-Pattern:

    @Carnage see, back in the day, frontend and backend were one thing, and the good devs kept the bad devs somewhat under control...

    Rose tinted glasses comes to mind. I've been inflicted with frontends from the 90-ies, they were horrible in a different way instead.
    Frontends are just horrible when they go past the trivial threshold. the current state of affairs with web application SPAs went even more derp, but it's not like anything else is anywhere near good.


  • Banned

    @Carnage said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    @Carnage see, back in the day, frontend and backend were one thing, and the good devs kept the bad devs somewhat under control...

    Rose tinted glasses comes to mind.

    It was kind of a meta-joke, like, remember what the code was like back then, and imagine if the good devs didn't try to stop it...



  • @Gąska said in Favorite Anti-Pattern:

    @Carnage said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    @Carnage see, back in the day, frontend and backend were one thing, and the good devs kept the bad devs somewhat under control...

    Rose tinted glasses comes to mind.

    It was kind of a meta-joke, like, remember what the code was like back then, and imagine if the good devs didn't try to stop it...

    Yeah... Good code had a different meaning back then as well.


  • Notification Spam Recipient

    @Carnage said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    @Carnage said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    @Carnage see, back in the day, frontend and backend were one thing, and the good devs kept the bad devs somewhat under control...

    Rose tinted glasses comes to mind.

    It was kind of a meta-joke, like, remember what the code was like back then, and imagine if the good devs didn't try to stop it...

    Yeah... Good code had a different meaning back then as well.

    :belt_onion: I'd be happy if most people knew the difference between a linkedlist and an arraylist before QA start asking why an operation has slowed to to a crawl. If we can jump the hurdle of people understanding how basic data structures work I'll call the new decade a success.


  • Discourse touched me in a no-no place

    @DogsB said in Favorite Anti-Pattern:

    I'd be happy if most people knew the difference between a linkedlist and an arraylist before QA start asking why an operation has slowed to to a crawl.

    But it passes all the unit tests!!! WHARRRGRBL!!!



  • @DogsB said in Favorite Anti-Pattern:

    @Carnage said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    @Carnage said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    @Carnage see, back in the day, frontend and backend were one thing, and the good devs kept the bad devs somewhat under control...

    Rose tinted glasses comes to mind.

    It was kind of a meta-joke, like, remember what the code was like back then, and imagine if the good devs didn't try to stop it...

    Yeah... Good code had a different meaning back then as well.

    :belt_onion: I'd be happy if most people knew the difference between a linkedlist and an arraylist before QA start asking why an operation has slowed to to a crawl. If we can jump the hurdle of people understanding how basic data structures work I'll call the new decade a success.

    Data structures? Just store it as JSON in a string!


  • Java Dev

    @DogsB said in Favorite Anti-Pattern:

    If we can jump the hurdle of people understanding how basic data structures work I'll call the new decade a success.

    Which decade would that be? 2000s or 2010s?


  • Notification Spam Recipient

    @PleegWat said in Favorite Anti-Pattern:

    @DogsB said in Favorite Anti-Pattern:

    If we can jump the hurdle of people understanding how basic data structures work I'll call the new decade a success.

    Which decade would that be? 2000s or 2010s?

    I'd be happy if it happened by the end of 2020.


  • 🚽 Regular

    @GOG said in Favorite Anti-Pattern:

    @PleegWat said in Favorite Anti-Pattern:

    The original author for that component must have been heavy on the 'no early return' coolaid. Which I agree with to a point; in a different component success returns in the middle of a function have bit me bad.

    Does "verify your assumptions before you start working" break "no early return", though? I always considered it an exception to the general rule.

    I'm okay with "verify your assumptions while in the middle of working" as well.

    Well, I'll be honest: I'm okay with early returns anywhere, as long as they make sense and improve readability. More often that not, that means returning early failures.


  • Trolleybus Mechanic

    @Zecc I'm a firm believer in "throw early", which I extend to mean "have the throw statements show up in the code as early as makes sense".

    Look at the function in question: if I want to understand what's happening here, I would much prefer to know that the function throws if conition1, condition2 or condition3 aren't met before I dive into the weeds of "what happens if everything is a-ok".

    Honestly, even flipping the ifs would be an improvement:

    function dothing( ... )
    {
        if(!condition1)
        {
            throw
        }
        else
        {
            if(!condition2)
            {
                throw
            }
            else
            {
                if(!condition3)
                {
                    throw
                }
                else
                {
                     // do the actual work
                }
            }
        }
    }
    

    It's strictly worse than just using a bunch of if ... throw statements up front, but at least you'll immediately know you'll get an exception if any of the conditions aren't met.

    Looking at it now, the other benefit of this over the original approach is that the meat of each level of nesting is brought to the fore. By the time you reach the innermost level, you know all there is to know about the code.


  • Banned

    @GOG said in Favorite Anti-Pattern:

    @Zecc I'm a firm believer in "throw early", which I extend to mean "have the throw statements show up in the code as early as makes sense".

    Look at the function in question: if I want to understand what's happening here, I would much prefer to know that the function throws if conition1, condition2 or condition3 aren't met before I dive into the weeds of "what happens if everything is a-ok".

    I am the opposite. When I see some function, I want to first know its main purpose - the "happy" or "hot" path. All the abnormal conditions come second - they're usually not interesting. As a consequence, unless the nesting levels would be ungodly, I prefer to have the "ok" case listed first (reversing conditions if necessary), and everything else later.

    For how much I despise Scala, they did a few things right. One tiny feature that I really like is that all collections have both isEmpty and nonEmpty methods, so both ways are very readable - so it's almost never a problem to swap if with else.


  • Trolleybus Mechanic

    @Gąska said in Favorite Anti-Pattern:

    I am the opposite. When I see some function, I want to first know its main purpose - the "happy" or "hot" path.

    Why? I mean, I kinda get where you're coming from, but it seems to me that this requires a lot more head space than simply noting: function fails when any of condition1, condition2 or condition3 isn't met; moving on...



  • @Gąska

    A middle ground would be to code for the happy path and still have the early throw; starting the method off with a statement of the preconditions under which it is expected to be executed and what it will be assuming thenceforth, and the rest of the code doesn't have to be written to deal with the alternatives. That's the thinking behind the sort of verification method that was badly done in:

    State positively what needs to be the case, throwing if it's not. Then code knowing that what should be the case is. The verification assertions should be distinguishable enough from the meat that they're easy to skip over when reading.



  • @GOG said in Favorite Anti-Pattern:

    Honestly, even flipping the ifs would be an improvement:

    Whenever there's an if ... else construct with one short / trivial branch and one longer or more complex branch, I like to have the short branch first so that I still remember what the condition was all about when I hit the "else".


  • Banned

    @GOG said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    I am the opposite. When I see some function, I want to first know its main purpose - the "happy" or "hot" path.

    Why? I mean, I kinda get where you're coming from, but it seems to me that this requires a lot more head space than simply noting: function fails when any of condition1, condition2 or condition3 isn't met; moving on...

    Because the "happy" path is hit 99% of the time. And if it's not, some other "hot" path is. It's quite rare for errors to be the norm and for those errors to vary. I found myself wondering about particular error condition only when I'm debugging something and see a particular error thrown/reported in logs.



  • @Gąska said in Favorite Anti-Pattern:

    It's quite rare for errors to be the norm

    Have you forgotten where you are? 🐠


  • Banned

    @ixvedeusi said in Favorite Anti-Pattern:

    @GOG said in Favorite Anti-Pattern:

    Honestly, even flipping the ifs would be an improvement:

    Whenever there's an if ... else construct with one short / trivial branch and one longer or more complex branch, I like to have the short branch first so that I still remember what the condition was all about when I hit the "else".extracting a new method should be your immediate next step.

    But yes, if that's impossible, having the shorter on top (assuming they aren't both short enough) is better than the alternative.



  • @Gąska said in Favorite Anti-Pattern:

    .extracting a new method should be your immediate next step

    I knew you'd gonna say that and I still have no clue what advantage you think you might gain from that. All it does is provide you with another opportunity to lose track of what you're reading, because now, instead of just continuing to read, you'll have to go hunt down the definition of that function to find out what it actually does.


  • Banned

    @ixvedeusi said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    .extracting a new method should be your immediate next step

    I knew you'd gonna say that and I still have no clue what advantage you think you might gain from that.

    If you have a non-trivial if/else tree and non-trivial action to complete in "then", that's a lot of cognitive load at once. If you extract the method, you now have a trivial single method call in your "then", and separately in another context, your non-trivial action has no error conditions because they've been checked earlier and are now invariants. That's both more readable and makes it easier to follow the logic, especially since I rarely find myself wanting to know "what this function does when everything goes well" and "what errors are reported under what conditions" at the same time.



  • @Zecc said in Favorite Anti-Pattern:

    Well, I'll be honest: I'm okay with early returns anywhere, as long as they make sense and improve readability.

    A good rule of thumb is that early returns shouldn't be in deeply nested scopes that you just glance over while visually scanning the function.


  • Banned

    @dfdub an even better rule of thumb is to not have deeply nested scopes in the first place.



  • @Gąska said in Favorite Anti-Pattern:

    separately in another context, your non-trivial action has no error conditions because they've been checked earlier and are now invariants. That's both more readable and makes it easier to follow the logic

    No it's not, because it's still the same code, at best the level of indenting has changed a bit. However it's now divorced from its context, so if you want to verify what these invariants are that this code block depends on (for example because you need to verify that you didn't miss any), you now need to hunt down the calling site (and pray there's either still only one of them or all of them verify the same set of invariants). Code that doesn't specify what assumptions it's based on is the wurst. The most reliable and explicit way to specify these is by preceding the code with more code that checks them.

    The only time separating out parts of a function into a new function actually improves readability is when your original function was mixing up levels of abstractions. In that case, that's your problem and that thing should have been its own, independent function from the start, anyway.


  • Banned

    @ixvedeusi interestingly, complicated conditions followed by a complicated "then" branch is where I see levels of abstraction mixed up the most.


  • 🚽 Regular

    @Gąska said in Favorite Anti-Pattern:

    @dfdub an even better rule of thumb is to not have deeply nested scopes in the first place.

    While I'm mostly with you on this, sometimes when complexity is, well, complex enough, I find adding #regions is preferable to adding single use functions that are going to be called in a single place and require passing in a bunch of context.
    That is: there are cases where relying on your editor's cold-folding in preferable to having your princess in another castle and needing to navigate away from where you are when trying to follow the code.

    Your taste may vary though. I know mine varies according to my mood at the time.

    (This is coming in part from my current frustration dealing with an improperly factored codebase, where the single responsibility principle seems to have been interpreted as "a single god object is responsible for everything")



  • @Gąska said in Favorite Anti-Pattern:

    interestingly, complicated conditions followed by a complicated "then" branch is where I see levels of abstraction mixed up the most.

    That's undeniably true. Still shouldn't confuse cause and symptom, though.


  • Banned

    @Zecc said in Favorite Anti-Pattern:

    This is coming in part from my current frustration

    I figured as much. Everyone's programming preferences are shaped by some traumatic event. For me, it's getting lost in the sea of 500-line long methods that have very obvious split points and yet they were never split apart.

    I think of code this way. A function is a named sequence of instructions. A code block is an anonymous sequence of instructions. Anonymous classes are cool and all, but sometimes it really helps to name your things, even if they're only ever used once.


  • 🚽 Regular

    @Gąska said in Favorite Anti-Pattern:

    I think of code this way. A function is a named sequence of instructions. A code block is an anonymous sequence of instructions. Anonymous classes are cool and all, but sometimes it really helps to name your things, even if they're only ever used once.

    Nice way of putting it.

    I just want to point out #regions are a valid way to name anonymous code blocks.


  • Banned

    @Zecc yeah, I guess. Although I don't work in .Net much and don't think other ecosystems have strong support for it. And as I mentioned, I often find it that, out of the several standard reasons why you'd want to extract a method, if one of them applies, the others usually apply too - so any of them is a good and mostly equivalent heuristic.


  • Java Dev

    @GOG said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    I am the opposite. When I see some function, I want to first know its main purpose - the "happy" or "hot" path.

    Why? I mean, I kinda get where you're coming from, but it seems to me that this requires a lot more head space than simply noting: function fails when any of condition1, condition2 or condition3 isn't met; moving on...

    It also makes it easier to ascertain which condition failure a message corresponds to.


  • Discourse touched me in a no-no place

    @ixvedeusi said in Favorite Anti-Pattern:

    The only time separating out parts of a function into a new function actually improves readability is when your original function was mixing up levels of abstractions. In that case, that's your problem and that thing should have been its own, independent function from the start, anyway.

    The two cases where it really helps are:

    1. Naming a complex composite condition. Fortunately I don't hit this sort of thing very often, but when I do it's awful so putting the awful in its own little prison away from the rest of the code helps a lot.
    2. Breaking out the actual core functionality out of a bunch of obscuring wrapping. The wrapping could be things like error handling or looping or stuff like that. Often this allows you to make the flow of that core function be a lot simpler while also highlighting that the rest of the code is just doing precondition checks, loop control and error handling.

    In both cases, it's about reducing the amount of code that you actually have to understand at once. (If you're in a language with decent inlining, this increase in understandability is actually effectively free at runtime. What's not to like?)


  • Trolleybus Mechanic

    @Gąska said in Favorite Anti-Pattern:

    @GOG said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    I am the opposite. When I see some function, I want to first know its main purpose - the "happy" or "hot" path.

    Why? I mean, I kinda get where you're coming from, but it seems to me that this requires a lot more head space than simply noting: function fails when any of condition1, condition2 or condition3 isn't met; moving on...

    Because the "happy" path is hit 99% of the time. And if it's not, some other "hot" path is. It's quite rare for errors to be the norm and for those errors to vary. I found myself wondering about particular error condition only when I'm debugging something and see a particular error thrown/reported in logs.

    The thing is, whenever I'm analysing a new piece of code whenever I see a throw I immediately lose interest. Once the code throws, it's someone else's problem. This means that front-loading failure modes allows me to ignore them pretty much completely, aside from noting that they exist.

    Going back to my revision of the example code, I can skim over the nested failure conditions without paying them any mind, read the real meat of the function and only have a bunch of braces on the end.

    If the failures are back-loaded, as in the original example, reading through the code goes something like "if condition1 is true check condition2 (but what if condition1 is false?) and if it is true check conditon3 (but what if condition2 is false? and what about condition1) and if that is true start doing things (but what happens if any of the conditions weren't met?); oh, here's a bunch of throws on the end, where's the thing that triggers those?"

    If the above reads like a mess, it's because the code is a mess.

    Compare and contrast: "'condition1 not met? throw; condition2 not met? throw; condition3 not met? throw; Do things."


  • Discourse touched me in a no-no place

    @GOG said in Favorite Anti-Pattern:

    Compare and contrast: "'condition1 not met? throw; condition2 not met? throw; condition3 not met? throw; Do things."

    Precondition checking is great! It's one of those things that is known to make code better.


  • Banned

    @GOG modern IDEs allow you to fold a code block brace-to-brace, if that's what you're complaining about. And if the "meat" was just 1 or 2 lines because the rest was extracted to a separate method, there wouldn't be a problem to start with.

    Maybe my opinion is influenced by how much functional programming I've been doing lately. We don't use many exceptions here; usually we don't have throw;, but rather else None several times. And it's much more awkward to read if (condition) None else actuallyInterestingStuff();.


  • Discourse touched me in a no-no place

    @Gąska said in Favorite Anti-Pattern:

    We don't use many exceptions here; usually we don't have throw;, but rather else None several times.

    The type theory of a language with exceptions really isn't any more complex than that of a language that uses an Option type/monad, except that exceptions are more explicit about capturing information about the nature of the problem that caused the failure (which can be useful or not). I've never been exactly sure why exceptions have been treated with such distrust by so many functional language makers.


  • Java Dev

    @dkf said in Favorite Anti-Pattern:

    @ixvedeusi said in Favorite Anti-Pattern:

    The only time separating out parts of a function into a new function actually improves readability is when your original function was mixing up levels of abstractions. In that case, that's your problem and that thing should have been its own, independent function from the start, anyway.

    The two cases where it really helps are:

    1. Naming a complex composite condition. Fortunately I don't hit this sort of thing very often, but when I do it's awful so putting the awful in its own little prison away from the rest of the code helps a lot.
    2. Breaking out the actual core functionality out of a bunch of obscuring wrapping. The wrapping could be things like error handling or looping or stuff like that. Often this allows you to make the flow of that core function be a lot simpler while also highlighting that the rest of the code is just doing precondition checks, loop control and error handling.

    In both cases, it's about reducing the amount of code that you actually have to understand at once. (If you're in a language with decent inlining, this increase in understandability is actually effectively free at runtime. What's not to like?)

    Breaking up a function also helps by emphasising variable scoping, if you're in a language which does not declare variables or in a coding style which requires such to be done at the top of a function.


  • Trolleybus Mechanic

    @Gąska said in Favorite Anti-Pattern:

    @GOG modern IDEs allow you to fold a code block brace-to-brace, if that's what you're complaining about. And if the "meat" was just 1 or 2 lines because the rest was extracted to a separate method, there wouldn't be a problem to start with.

    Maybe my opinion is influenced by how much functional programming I've been doing lately. We don't use many exceptions here; usually we don't have throw;, but rather else None several times. And it's much more awkward to read if (condition) None else actuallyInterestingStuff();.

    As said previously, my preference is to get rid of else clauses altogether, if at all possible. So my typical code would look something like:

    {
        if (!assumption1) throw;
        if (!assumption2) throw;
        if (!assumption3) throw;
    
        // actual code goes here
    }
    

    The benefit of this approach is that you can often write these as one-liners, which helps readability/skipping.

    Sure, a decent editor will allow you to fold code blocks. What if you ain't got a decent editor on hand? I find that it helps to write code in a way that will make it easy to read under whatever conditions - including as hard copy (but I'm old-school).

    That said, depending on the language (my weapon of choice is C#) the optimum approach may be different. "If (condition) DoStuff() else None;" isn't obviously worse (and may well be better) than "If (!condition) None else DoStuff();" although I would still look to see if I can write "If (!condition) None; DoStuff();" (not familiar with the language you have in mind, so I have to assume it would make sense).

    If nothing else, any way of getting rid of nested code paths is worth exploring.



  • @GOG said in Favorite Anti-Pattern:

    Sure, a decent editor will allow you to fold code blocks. What if you ain't got a decent editor on hand?

    Install something like Notepad++. It's a decent text editor, and you can fold code blocks in it.

    @GOG said in Favorite Anti-Pattern:

    That said, depending on the language (my weapon of choice is C#) the optimum approach may be different. "If (condition) DoStuff() else None;" isn't obviously worse (and may well be better) than "If (!condition) None else DoStuff();" although I would still look to see if I can write "If (!condition) None; DoStuff();" (not familiar with the language you have in mind, so I have to assume it would make sense).

    That's not going to compile. It should be "if". :tro-pop:

    Seriously, though, that's almost always feasible. At least, as long as there isn't something after the if/else, like "if (condition) DoStuff() else None; DoOtherStuff();"



  • @Gąska said in Favorite Anti-Pattern:

    @Zecc said in Favorite Anti-Pattern:

    This is coming in part from my current frustration

    I figured as much. Everyone's programming preferences are shaped by some traumatic event. For me, it's getting lost in the sea of 500-line long methods that have very obvious split points and yet they were never split apart.

    I can trace my philosophy of code working correctly but being ugly back to my first job and a particular 32000 lines long function that the senior devs were afraid to touch because it was really dumb, and repetitive so whenever you poked at it, something unrelated broke. Search and replace programming was the way to go. And pray someone didn't manually tweak a single place of repetition.

    These days, I rewrite stuff because I deem it hard to grasp, or longwinded code broken, even if it passes tests.

    I think of code this way. A function is a named sequence of instructions. A code block is an anonymous sequence of instructions. Anonymous classes are cool and all, but sometimes it really helps to name your things, even if they're only ever used once.

    I think the same way. I also have a very un-javaesque way of trying to come up with succinct ways to name things and often letting context provide part of the meaning for the name, instead of just typing out a descriptive sentence.


  • Trolleybus Mechanic

    @abarker said in Favorite Anti-Pattern:

    Install something like Notepad++. It's a decent text editor, and you can fold code blocks in it.

    I know. That's why (among other things) I used the word "editor" rather than "IDE".

    Nevertheless, you're still assuming I am free to install Notepad++ wherever it is I need it. What if this assumption turns out not to hold?



  • Then TRWTF is that your job is terrible ; the inability to use code blocks is just a side effect.


  • Banned

    @dkf said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    We don't use many exceptions here; usually we don't have throw;, but rather else None several times.

    The type theory of a language with exceptions really isn't any more complex than that of a language that uses an Option type/monad, except that exceptions are more explicit about capturing information about the nature of the problem that caused the failure (which can be useful or not). I've never been exactly sure why exceptions have been treated with such distrust by so many functional language makers.

    It's not about types - it's about convenience. Explicit failure objects compose much better than your typical implementation of exceptions. Here's an example - I'll use Scala types but I think it should be clear enough for everyone familiar with any flavor of functional programming.

    Let's say you have a List[() -> Either[Foo, Error]]and you want to call the functions and get Either[List[Foo], List[Error]]. It's very easy (although somewhat verbose, so I'll skip it) to do with a map to call the functions and a fold to accumulate the Foos or, in case or error, drop them and accumulate Errors instead.

    Now, consider the equivalent List[() -> Foo throws ErrorException]. How do you do it?

    Bonus task

    How do you do it without converting to Either[Foo, Error] or equivalent at any point?

    That's why functional programmers don't like throwing exceptions. They're just harder to work with. And as you pointed out, returning error early is equivalent to throwing, so it's not like they lose anything either.


  • Java Dev

    @Gąska said in Favorite Anti-Pattern:

    @dkf said in Favorite Anti-Pattern:

    @Gąska said in Favorite Anti-Pattern:

    We don't use many exceptions here; usually we don't have throw;, but rather else None several times.

    The type theory of a language with exceptions really isn't any more complex than that of a language that uses an Option type/monad, except that exceptions are more explicit about capturing information about the nature of the problem that caused the failure (which can be useful or not). I've never been exactly sure why exceptions have been treated with such distrust by so many functional language makers.

    It's not about types - it's about convenience. Explicit failure objects compose much better than your typical implementation of exceptions. Here's an example - I'll use Scala types but I think it should be clear enough for everyone familiar with any flavor of functional programming.

    Let's say you have a List[() -> Either[Foo, Error]]and you want to call the functions and get Either[List[Foo], List[Error]]. It's very easy (although somewhat verbose, so I'll skip it) to do with a map to call the functions and a fold to accumulate the Foos or, in case or error, drop them and accumulate Errors instead.

    Now, consider the equivalent List[() -> Foo throws ErrorException]. How do you do it?

    Bonus task

    How do you do it without converting to Either[Foo, Error] or equivalent at any point?

    That's why functional programmers don't like throwing exceptions. They're just harder to work with. And as you pointed out, returning error early is equivalent to throwing, so it's not like they lose anything either.

    Whether you're throwing an exception or returning an error object seems irrelevant for the subject at hand. You were actually suggesting returning None, and that specifically is what probably triggered the remark about keeping error context.

    When processing a list, it's definitely true that an either-or return allows returning multiple errors, while throwing exceptions does not (it naturally returns the first error and doesn't even get to the rest). Whether that is a good thing depends on the circumstance.

    I tend to work in C, so I'll probably drop the error in a log file immediately, then simply return an error status. In a loop wrapper, I can then decide whether I cascade an error immediately, or save the fact an error occurred and process the other elements first (possibly yielding more errors).

    And I do believe functional languages tend to make it easy to cascade error returns. Many C-like languages do not, so a function level which passes through an exception will have no code for it at all, while one which uses error returns requires 2-3 lines specifically to test if there was an error return and cascade it. If there are many calls which cascade errors, that adds up.


  • Banned

    @PleegWat said in Favorite Anti-Pattern:

    Whether you're throwing an exception or returning an error object seems irrelevant for the subject at hand. You were actually suggesting returning None, and that specifically is what probably triggered the remark about keeping error context.

    We can :bikeshed: all day long about what triggered whom to say what, but the main point is, there are valid reasons why functional programmers avoid exceptions, and if (...) None else ... weirds me out.


Log in to reply