5 ms·
Undoing someone's copy-pasted for-loops and while-loops is called "factoring" and undoing someone's bad abstractions is called "re-factoring" and is about 10x a
by titzer 4y ago
Undoing someone's copy-pasted for-loops and while-loops is called "factoring" and undoing someone's bad abstractions is called "re-factoring" and is about 10x as slow. At this point in my career I have seen way more crappy abstractions and needlessly abstracted code than grug-brain code.
And, to be brutally honest, as much as I love those functional combinators, first-class functions, streams, etc, they suck to reason about.
Sometimes loops are better!
- eurasiantiger 4y agoI think you found the head of the nail, so let me hit it: ”Simple” code is simple to reason about, but it is not as expressive. ”Complex” code is just as simple to reason about, but being more expressive, it requires more intimate knowledge of the language features and abstractions used. Then there is ”bad” code, which is confusing for reasons other than domain complexity. Sometimes complexity just gets handed to a developer even if they say no, and that doesn’t make their complex code bad code.
- Izkata 4y ago> ”Complex” code is just as simple to reason about, but being more expressive, it requires more intimate knowledge of the language features and abstractions used. > Then there is ”bad” code, which is confusing for reasons other than domain complexity. I've previously seen these two summarized as "the difference between complex and complicated".
- stonecolddevin 4y agoI personally almost always use loops (in Java) unless I know the dataset being dealt with is small, unless I'm writing stuff in Scala where maps and flatMaps are first class, but diving into Scala is its own can of worms.
- lmm 4y ago> And, to be brutally honest, as much as I love those functional combinators, first-class functions, streams, etc, they suck to reason about. > Sometimes loops are better! That I think is backwards. A loop could be doing literally anything - it probably is futzing with global variables - so there's no way to reason about it except by executing the whole thing in your head. A map (or mapA) or a fold (or foldM) or a filter or a scan is much more amenable to reasoning, since it's so much more specific about what it's doing even without looking at the body.
- andyferris 4y agoI like to write loops where they follow the functional map/filter/reduce paradigm where they don’t mutate anything except some initial variables you “fold” over (defined immediately prior to the loop) and which are treated immutable (or uniquely owned) after the loop. I find this has good readability and by containing the mutation you can reason about it “at a distance” quite simply since further away it’s for-intents-and-purposes pure code. What you might lose are the compiler-enforced guarantees that a functional language gives you. Some languages give you the best of both worlds - with rust you could put this in a pure function (immutable inputs, owned outputs) and the borrow checker even reasons about things like this within a function body.
- galaxyLogic 4y agoI think a loop is ok if it only modifies variables whose scope is the loop. And that's what map() basically does.
- philosopher1234 4y agoNot true in reality, so your theory must be wrong.
- lmm 4y agoI've been doing this professionally for over a decade and it's been true in my experience, shrug.
- titzer 4y ago> it probably is futzing with global variables The opposite is true in my experience. Loops working over variables local to a function is a lot easier to reason about then a series of intricately intertwined lambdas, if they share state. If everything you do is functional then a series of pipelines might be alright, but it might not. If the functions are spread all over the place it can take a larger amount of mental load to keep all the context together. Code should permit local reasoning, and anytime that is obscured, rather than helped by, abstractions, we incur additional cognitive load.
- joshuamorton 4y agoI've seen a massive, "simple", abstraction-free function of 500-1000 lines take smart engineers quarters to safely "factor". I've seen this same thing happen a handful of times. Big spaghetti function requires someone to build a new test suite from scratch so that they can refactor safely, because everything ends up having spooky surprising distant effects, because there isn't any abstraction to contain things. They're dead simple, there's no abstraction, its just loops and variables (and state, which is the real killer). But they're impossible to reason about as a result.
- titzer 4y agoSure, this happens. But imagine it was 10 times that size and factory poorly. If you had the same lack of tests, I'd contend it'd be a worse nightmare, because you have to first reverse engineer the confusing old design, unconfuse it, and then incrementally refactor it. You can write bad code in any language with any constructs. The more code you do write the worse bad ideas just metastasize like cancer.