DEV Community

When is nesting good or neutral?

Ben Halpern on January 06, 2020

I was reading this post... Please don't nest promises Basti Ortiz ・ Jan 6 '20 #javascr...
Collapse
 
dmfay profile image
Dian Fay

I'm kind of confused by the premise. Certainly one should avoid driving nails with the butt of a hacksaw if there's anything better to hand. That doesn't make hacksaws bad. Hierarchization and nesting are organizational tools; sometimes they suit, sometimes they don't.

Collapse
 
cishiv profile image
Shivan Moodley • Edited

When it comes to conditional logic, I always try to ask myself 'how important is readability for this code block' i.e is this something that would change as my project progresses, and further than that, am I the only maintainer of the code? If it is highly volatile code (subject to change often) and if I am not the sole maintainer, then I err on the side of rewriting nested conditionals. I don't think nested blocks are generally bad, but I can't think of a case where they outperform inversion of conditions to single statements, except I guess in terms of mental overhead.

So for instance I would tend to write this

function conditionalsOrNo() throws AnException {
    if (!codeIsNotVolatile) throws new YouShouldRefactorException()
    if (!codeIsMaintainedByMe) throws new WhyAreYouTryingToCauseYourselfPainException()
    return writeConditionals()
}

Instead of this

function conditionalsOrNo() throws AnException {
    if(codeIsNotVolatile) {
        if(codeIsMaintainedByMe) {
            return writeConditionals()
        } else {
            throws new YouShouldRefactorException()
        }
    } else {
        throws new WhyAreYouTryingToCauseYourselfPainException()
    }
}

So I guess the question is more down to the conditions the code exists in, rather than the immediate computational benefits of nested vs alternatives?

This is a really great question

Collapse
 
jbristow profile image
Jon Bristow • Edited

And now with monads....

fun conditionalsOrNo(maybeCode: Either<Error, Code>) = maybeCode.map(Code::writeConditionals)

Please note that this example is bad because it appears to rely on side effects

Collapse
 
cishiv profile image
Shivan Moodley

I have googling to do it seems

Thread Thread
 
jbristow profile image
Jon Bristow

Please note that the brevity of my example relies upon a strong type system with at least basic generics support.

Interesting google terms:

Discriminated Unions
Functional programming
Monads
Haskell (or Hindley-Milner type systems)
Lodash (decent javascript functional library, though this may be a little opaque unless you experiment with a FP language for a while)

Thread Thread
 
cishiv profile image
Shivan Moodley

Thank you for this. I'll add it to this weekends reading list.

Collapse
 
lito profile image
Lito

And what about:

function conditionalsOrNo() throws AnException {
    if (!codeIsNotVolatile) {
        throws new YouShouldRefactorException();
    }

    if (!codeIsMaintainedByMe) {
        throws new WhyAreYouTryingToCauseYourselfPainException();
    }

    return writeConditionals();
}

Easy to read and no nested code.

Collapse
 
cishiv profile image
Shivan Moodley

That works too. The lack of braces is just my lazy inner java dev showing

Collapse
 
codemouse92 profile image
Jason C. McDonald

From a Python perspective, we are informed by the Zen of Python

Flat is better than nested.

Python provides many patterns that allow one to avoid nesting in most cases. Of course, it's not altogether unavoidable, but you should try to avoid going any deeper than 2-3 levels, functions included.

Collapse
 
_hs_ profile image
HS

It's (sometimes) horrible how frameworks/tool generate HTML but also necessary in some cases and not a big deal for developers as those tools handle those lines themselves. On the other hand if written by human it is something that should be avoided as much as possible.

  1. readability
  2. sometimes it's even performance issue but only in case of data structures (html,xml,json being included as some form of "data structure"), I don't see that many benefits

Reactive style made it possible to have no idea what you wrote last month, let alone last year. Now it can be written better etc but there's a reason async/await was added.

Although it's a bit uglier and harder to write. with microservices reactive style is good enough in terms of clearness as your code is split. Now a lot of people would go with Go (pun intended), Kotlin etc for those prefix_routines but

this.map(is -> is.standard()).flatMap(x -> "As many languages use same sytanx")


which is quite good when having more than 1 technology. Now you do write either more functions or less clear code and there's no "a matter of taste" in it as it turns out you can take a deep look at previous iterative codes and compare to reactive ones and the actual problem starts with nesting

result.flatMap(x -> anotherPublishFunction(x).map(y -> { y.setSomething("lalal"); return y })).zipWith(...)

If you try to write this more clearly you end up either with more lines or more functions which convert stuff, zip, publish, toIterrable etc.

You need to deal with complex/ugly code. You need to couple some stuff. You need iterative approach sometimes. You need nesting sometimes. It's just a thing we have to live with :D

Collapse
 
jbristow profile image
Jon Bristow • Edited

Nesting conditional logic is not always bad.

Nesting promises is always less clear than it could be.

Consider:

  • alpha takes a url and returns a promise of an http response.
  • beta takes a url, calls alpha, and returns the promise of some heavy async calculation of an http response.

Versus in un-nested:

  • alpha takes a url and returns a promise of an http response.
  • beta takes a http response and returns a promise of some heavy async calculation of that response

All nesting can be expressed this way. (Convince me otherwise)

This property of monads is why Clojure has a threading macro, Haskell has pointfree, and most ML derivatives have a concept of piping.

E.g. you can define function gamma that is expressed as alpha | beta (Aka beta(alpha(x))) that just takes a url and spits out a single promise of a processed http response, but still isn’t nesting!

Collapse
 
dannypsnl profile image
ζž—ε­η―†

A few cases, sometimes we cannot extract out a function because the abstraction is bad for maintaining, however, we have a complex condition like: if a && b && c, in case I prefer if a { if b { if c {} else {} } else {} } else {}. But most of the time I can find out how to extract it as a new function, but if really cannot do that, simple-ugly is better than elegant-hiding-the-problem.

Collapse
 
sebvercammen profile image
SΓ©bastien Vercammen

All scoping is nesting.

Collapse
 
nickholmesde profile image
Nick Holmes

I would suggest that scoping is data structure nesting, not control structure nesting, and therein lies a vitally important difference.

Collapse
 
sebvercammen profile image
SΓ©bastien Vercammen

Everything's a data structure.

Collapse
 
sebbdk profile image
Sebastian Vargr

I depends on verbosity in my case.

In nested code i find my self often looking for where indented sections end.

If i don't have to do look for where things end, then i'm find.

Consequently, this is usually around 2-3 indentations.

After that i often start to run our of useful vertical screen-space and the moment a indented section cross into where i cannot see it, then it becomes a problem to track again.

Collapse
 
nickholmesde profile image
Nick Holmes

Nesting control structures quickly creates very many possible code paths, which makes the code harder to read, harder to work on, and, perhaps most importantly, harder to test. For this reason, it should be avoided/minimized.

Collapse
 
wolfhoundjesse profile image
Jesse M. Holmes

This is the answer I came for. :)

Collapse
 
pavelloz profile image
PaweΕ‚ Kowalski

Nesting when you write is very useful, given its obvious in your editor.

Nesting in output, probably not useful at all.

Nesting when writing logic, i would say, i try to do as little of that as possible, because parsing that in my brain is a chore.

Collapse
 
somedood profile image
Basti Ortiz

If you don't mind, I'll "repost" my reply to you from the article in question. πŸ˜‰

Hmm... that's a good question. To that I will argue no, there is no such thing as a "good kind of nesting" in any programming language, even for markup.

I would like to expound on the specific case of HTML because deeply nested markup can be an indication of a poorly planned layout structure teeming with so-called "CSS hacks". On the discussion of performance, deeply nested markup also takes it toll on the amount of memory the DOM consumes. All those wrapper divs may seem innocent at first, but having a bunch of them will definitely be taxing for lower-end devices, especially on mobile.

For data-oriented nesting such as in XML and JSON, perhaps I can be more lenient, but even then, deeply nested markup requires a bunch of computer resources just to traverse, not even at the data processing stage yet.

Collapse
 
jacoby profile image
Dave Jacoby

I recently took a function with many nested ifs to modify two variables and rewrote it to return immediately instead. It was much more readable but didn't work when put into testing, but the point is that nesting makes things more complicated and harder to read.

That can be necessary; required behaviors can be complex. But it's kinder to your brain and those of the next dev to flatten.

Collapse
 
elmuerte profile image
Michiel Hendriks

Without proper deep levels of nesting how can I post my hadoken gif?

Collapse
 
ben profile image
Ben Halpern

πŸ˜„

Collapse
 
aumayeung profile image
John Au-Yeung

The less nesting the better.

It's jist so hard to read deeply nested code.

And I think the whole point if Promises is to eliminate callback hell, which is deeply nesting callbacks.

Collapse
 
rafaelcastrocouto profile image
@racascou

There are situations where nesting is necessary, in all other you will do better with a flat solution.