The Unix Heritage Society mailing list
 help / color / mirror / Atom feed
* [TUHS] margins and indenting, and arbitrary c rules
@ 2017-11-09  8:07 Steve Simon
  0 siblings, 0 replies; 7+ messages in thread
From: Steve Simon @ 2017-11-09  8:07 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 320 bytes --]


At the risk of stirring up another hornets nest...

I never accepted the Microsoft

“write functions for maximal indentation, with only one return”

and stick to

“get out if the function asap, try to have success at the bottom of the function”

style.

this means my code is usually fairly narrow.

-Steve





^ permalink raw reply	[flat|nested] 7+ messages in thread

* [TUHS] margins and indenting, and arbitrary c rules
  2017-11-09 15:31 Noel Chiappa
@ 2017-11-10 18:41 ` Ian Zimmerman
  0 siblings, 0 replies; 7+ messages in thread
From: Ian Zimmerman @ 2017-11-10 18:41 UTC (permalink / raw)


On 2017-11-09 10:31, Noel Chiappa wrote:

> Now that I think about it, I may have subconciously absorbed this from
> Ken's and Dennis' code in the V6 kernel. Go take a look at it: more
> than one level of indent is quite rare in anything there (including
> tty.c, which has some pretty complex stuff in it).

It has also been the preferred coding style in Perl, I think right from
the beginning.  I'm sure it's written down somewhere, maybe even by
Larry Wall.

-- 
Please don't Cc: me privately on mailing lists and Usenet,
if you also post the followup to the list or newsgroup.
To reply privately _only_ on Usenet, fetch the TXT record for the domain.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [TUHS] margins and indenting, and arbitrary c rules
  2017-11-09 15:41 ` Don Hopkins
@ 2017-11-09 19:13   ` Ron Natalie
  0 siblings, 0 replies; 7+ messages in thread
From: Ron Natalie @ 2017-11-09 19:13 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 5121 bytes --]

The issue of structure is that return/break/continue can allow you to break the concept of structured code.

Your case doesn’t, but let’s use this example:

 

int foo() {

        if(this_condition) {

             if(that_condition) 

                return 0;

            executable_code();

      } else {
          more_executable_code();

     }

    return 1;

}

 

As simple as this one is, you could rewrite it to be structured but things aren’t usually that easy.

 

 

 

From: TUHS [mailto:tuhs-bounces@minnie.tuhs.org] On Behalf Of Don Hopkins
Sent: Thursday, November 9, 2017 10:41 AM
To: Noel Chiappa
Cc: tuhs at minnie.tuhs.org
Subject: Re: [TUHS] margins and indenting, and arbitrary c rules

 

 

On 9 Nov 2017, at 16:09, Noel Chiappa <jnc at mercury.lcs.mit.edu> wrote:


where xxx is a 'return', or a 'break', or a 'continue'. That way, you can
empty your brain once you hit that, and start with a clean slate.

 

Absolutely — you nailed it! Not only do guard clauses reduce indentation, but they also let you clear your mind and forget about as much as possible before running into the complex stuff you will need all of your short term memory to understand, and which is then more likely to fit on the screen. 

 

https://stackoverflow.com/questions/268132/invert-if-statement-to-reduce-nesting/8493256#8493256

 

Nested if statements have more “join” points where control flow merges back together at the end instead of bailing out early, and the deeper and deeper nesting breaks up visual patterns that would otherwise be apparent and wastes lots of space (on the page and in your head). 

 

“Guard clauses” with multiple returns can also make it easier to visually emphasize the symmetries and patterns in the code. 

 

I try to take every opportunity to break my code and expressions up across multiple lines to emphasize repetition, patterns and variations, and I always use parens to explicitly state which groupings I mean instead of depending on the reader to be a good enough programmer who has memorized all the precedence rules to infer what I mean and hope I didn’t slip up and make a mistake — I’m looking at YOU && and || !!!. (I’d fire any programmer who mixed && and || without parens just to show off what a hot-shot they were for remembering they have different precedence and trying to save a few bytes of disk space at the expense of readability.)

 

For example, instead of:

 

float length = sqrt(x * x + y * y + z * z);

 

I go:

 

float length =

    sqrt(

        (x * x) +

        (y * y) +

        (z * z));

 

To me, that’s like getting a “triple word score” and a “double word score" in scrabble, when I can arrange the code into pleasing patterns that explicitly shows the repetition and progression of two factors by three dimensions, and arranges the x's, y's and z's and *’s and +’s into three nice neat rows and columns reflecting the structure of the expression. 

 

The easier it is for your eyes to scan up and down the code to verify it’s right and detect errors, the better, and the following example shows how guard clauses can do that:

 

See how much easier it is to spot the bug:

 

float length = sqrt(x * x + x * y + z * z);

 

float length =

    sqrt(

        (x * x) +

        (x * y) +

        (z * z));

 

http://wiki.c2.com/?GuardClause

 

Here is a sample of code using guard clauses ...

  public Foo merge (Foo a, Foo b) {
    if (a == null) return b;
    if (b == null) return a;
    // complicated merge code goes here.
  }

Some style guides would have us write this with a single return as follows ...

  public Foo merge (Foo a, Foo b) {
    Foo result;
    if (a != null) {
      if (b != null) {
        // complicated merge code goes here.
      } else {
        result = a;
      }
    } else {
      result = b;
    }
    return result;
  }
 

This second form has the advantage that the usual case, the merge, is dealt with first. It also has a single exit as the last line of the routine which can be handy with some refactorings. It has the disadvantage of separating the exceptional conditions from their corresponding results which, in this case, makes it harder to see the symmetry of the conditions. It also buries the usual, and complicated, case inside a couple of layers of braces, which may make it harder to read.

 

The guards are similar to assertions in that both protect the subsequent code from special cases. Guards differ from assertions in that they make a tangible contribution to the logic of the method and thus cannot be safely omitted as part of an optimization. I borrowed the term guard from  <http://wiki.c2.com/?EwDijkstra> EwDijkstra when naming this pattern. The first form above hints at the elegance of his guarded commands though Dijkstra manages to save the single exit property in his code as well. --  <http://wiki.c2.com/?WardCunningham> WardCunningham

 

 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://minnie.tuhs.org/pipermail/tuhs/attachments/20171109/de989aa9/attachment-0001.html>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [TUHS] margins and indenting, and arbitrary c rules
@ 2017-11-09 16:03 Noel Chiappa
  0 siblings, 0 replies; 7+ messages in thread
From: Noel Chiappa @ 2017-11-09 16:03 UTC (permalink / raw)


    > From: Don Hopkins

    > https://stackoverflow.com/questions/268132/invert-if-statement-to-reduce-nesting/8493256

Thanks for that - interesting read. (Among other things, it explains where the
'only have one return, at the end' comes from - which I never could understand.)

    > Nested if statements have more 'join' points where control flow merges
    > back together at the end instead of bailing out early

Exactly. In high-level terms, it's much like having a ton of goto's. Yes, the
_syntax_ is different, but the semantics - and understanding how it works - is
the same - i.e. hard.

    Noel


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [TUHS] margins and indenting, and arbitrary c rules
  2017-11-09 15:09 Noel Chiappa
@ 2017-11-09 15:41 ` Don Hopkins
  2017-11-09 19:13   ` Ron Natalie
  0 siblings, 1 reply; 7+ messages in thread
From: Don Hopkins @ 2017-11-09 15:41 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 4461 bytes --]


> On 9 Nov 2017, at 16:09, Noel Chiappa <jnc at mercury.lcs.mit.edu> wrote:
> 
> where xxx is a 'return', or a 'break', or a 'continue'. That way, you can
> empty your brain once you hit that, and start with a clean slate.

Absolutely — you nailed it! Not only do guard clauses reduce indentation, but they also let you clear your mind and forget about as much as possible before running into the complex stuff you will need all of your short term memory to understand, and which is then more likely to fit on the screen. 

https://stackoverflow.com/questions/268132/invert-if-statement-to-reduce-nesting/8493256#8493256 <https://stackoverflow.com/questions/268132/invert-if-statement-to-reduce-nesting/8493256#8493256>

Nested if statements have more “join” points where control flow merges back together at the end instead of bailing out early, and the deeper and deeper nesting breaks up visual patterns that would otherwise be apparent and wastes lots of space (on the page and in your head). 

“Guard clauses” with multiple returns can also make it easier to visually emphasize the symmetries and patterns in the code. 

I try to take every opportunity to break my code and expressions up across multiple lines to emphasize repetition, patterns and variations, and I always use parens to explicitly state which groupings I mean instead of depending on the reader to be a good enough programmer who has memorized all the precedence rules to infer what I mean and hope I didn’t slip up and make a mistake — I’m looking at YOU && and || !!!. (I’d fire any programmer who mixed && and || without parens just to show off what a hot-shot they were for remembering they have different precedence and trying to save a few bytes of disk space at the expense of readability.)

For example, instead of:

float length = sqrt(x * x + y * y + z * z);

I go:

float length =
    sqrt(
        (x * x) +
        (y * y) +
        (z * z));

To me, that’s like getting a “triple word score” and a “double word score" in scrabble, when I can arrange the code into pleasing patterns that explicitly shows the repetition and progression of two factors by three dimensions, and arranges the x's, y's and z's and *’s and +’s into three nice neat rows and columns reflecting the structure of the expression. 

The easier it is for your eyes to scan up and down the code to verify it’s right and detect errors, the better, and the following example shows how guard clauses can do that:

See how much easier it is to spot the bug:

float length = sqrt(x * x + x * y + z * z);

float length =
    sqrt(
        (x * x) +
        (x * y) +
        (z * z));

http://wiki.c2.com/?GuardClause <http://wiki.c2.com/?GuardClause>

Here is a sample of code using guard clauses ...
  public Foo merge (Foo a, Foo b) {
    if (a == null) return b;
    if (b == null) return a;
    // complicated merge code goes here.
  }
Some style guides would have us write this with a single return as follows ...
  public Foo merge (Foo a, Foo b) {
    Foo result;
    if (a != null) {
      if (b != null) {
        // complicated merge code goes here.
      } else {
        result = a;
      }
    } else {
      result = b;
    }
    return result;
  }

This second form has the advantage that the usual case, the merge, is dealt with first. It also has a single exit as the last line of the routine which can be handy with some refactorings. It has the disadvantage of separating the exceptional conditions from their corresponding results which, in this case, makes it harder to see the symmetry of the conditions. It also buries the usual, and complicated, case inside a couple of layers of braces, which may make it harder to read.

The guards are similar to assertions in that both protect the subsequent code from special cases. Guards differ from assertions in that they make a tangible contribution to the logic of the method and thus cannot be safely omitted as part of an optimization. I borrowed the term guard from EwDijkstra <http://wiki.c2.com/?EwDijkstra> when naming this pattern. The first form above hints at the elegance of his guarded commands though Dijkstra manages to save the single exit property in his code as well. -- WardCunningham <http://wiki.c2.com/?WardCunningham>


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://minnie.tuhs.org/pipermail/tuhs/attachments/20171109/bfc8998d/attachment.html>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [TUHS] margins and indenting, and arbitrary c rules
@ 2017-11-09 15:31 Noel Chiappa
  2017-11-10 18:41 ` Ian Zimmerman
  0 siblings, 1 reply; 7+ messages in thread
From: Noel Chiappa @ 2017-11-09 15:31 UTC (permalink / raw)


    > I'd been moving in this direction for a while

Now that I think about it, I may have subconciously absorbed this from Ken's
and Dennis' code in the V6 kernel. Go take a look at it: more than one level
of indent is quite rare in anything there (including tty.c, which has some
pretty complex stuff in it).

I don't know if this was subconcious but deliberate, or concious, or just
something they did for other reasons (e.g. typing long lines took too long on
their TTY's :-), but it's very noticeable, and consistent.

It interesting that both seem to have had the same style; tty.c is in dmr/, so
presumably Dennis', but the stuff in ken/ is the same way.


Oh, one other trick for simplifying code structure (which I noticed looking
through the V6 code a bit - this was something they _didn't_ do in one place,
which I would have done): if you have

	if (<condition>) {
		<some complex piece of code>
		}
	<implicit return>
}

invert it:

	if (<!condition>)
		return;

	<some complex piece of code>
}

That gets that whole complex piece of code down one level of nesting.

     Noel


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [TUHS] margins and indenting, and arbitrary c rules
@ 2017-11-09 15:09 Noel Chiappa
  2017-11-09 15:41 ` Don Hopkins
  0 siblings, 1 reply; 7+ messages in thread
From: Noel Chiappa @ 2017-11-09 15:09 UTC (permalink / raw)


    > From: Steve Simon

    > At the risk of stirring up another hornets nest...

I usually try not to join in to non-history threads (this list has too much
random flamage on current topics, IMNSHO), but I'll make an exception here...

    > this means my code is usually fairly narrow.

I have what I think are somewhat unusual thoughts on nesting depth, etc, which
are: keep the code structure as simple as possible. That means no complex
nested if/then/else structures, etc (more below).

I'd been moving in this direction for a while, but something that happened a
long time ago at MIT crystalized this for me: a younger colleague brought me a
routine that had a complex nested if/etc structure in it, which had a bug; I
don't recall if he just wanted advice, or what, but I recall I fixed his
problem by..... throwing away half his code. (Literally.)

That really emphasized to me the downside of complexity like that; it makes it
harder to understand, harder to debug, and harder for someone else (or you
yourself, once you've been away from it for a while) to modify it. I've been
getting more formal about that ever since.


So, how did I throw away half his code? I have a number of techniques. I try
not to use 'else' unless it's the very best way to do something. So instead
of:

    if (some-conditional) {
       <some code>;
       }
      else {
       <some other code>;
       }      

(iterated, recursively) do:

    if (some-conditional) {
       <some code>;
       xxx;
       }

    <some other code>;

where xxx is a 'return', or a 'break', or a 'continue'. That way, you can
empty your brain once you hit that, and start with a clean slate.

This is also an example of another thing I do, which is use lots of simple
flow-control primitives; not go-tos (which I abhor), but loop exits, restarts,
etc. Again, it just makes things simpler; once you hit it, you can throw away
all that context/state in your mind.


Keep the control flow structure as simple as possible. If your code is several
levels of indent deep, you have much bigger problems than fitting it on the
screen.


I wrote up a complete programming style guide for people at Proteon working on
the CGW, which codified a lot of this; if there's interest, I'll see if I can
find a copy (I know I have a hardcopy _somewhere_).

     Noel


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-11-10 18:41 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-09  8:07 [TUHS] margins and indenting, and arbitrary c rules Steve Simon
2017-11-09 15:09 Noel Chiappa
2017-11-09 15:41 ` Don Hopkins
2017-11-09 19:13   ` Ron Natalie
2017-11-09 15:31 Noel Chiappa
2017-11-10 18:41 ` Ian Zimmerman
2017-11-09 16:03 Noel Chiappa

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).