go fmt and multi-line conditionals

14,298 views
Skip to first unread message

wmpalmer

unread,
May 21, 2012, 4:56:17 AM5/21/12
to golang-nuts
Hello,

I've been playing around with golang over the past couple of days, and
I noticed an oddity when using "go fmt" on files with "if" statements
whose conditions are spread across multiple lines. The formatting was
bizarre enough to me that I am wondering if multi-line conditionals
are just strongly discouraged in the go community.

go fmt seems to think the best way of presenting multi-line
conditionals is:

if conditionA &&
conditionB &&
conditionC {
bodyOfStatement()
}

this has two major drawbacks in my opinion:
1) "conditionA" is visually separated from "conditionB" and
"conditionC"
2) there is no visual separation between the "conditions" and the
"body"

After a brief discussion in #go-nuts on freenode, the suggestion of an
alternative formatting was made:

if conditionA &&
conditionB &&
conditionC {
bodyOfStatement()
}

which I am fairly neutral towards (or at least, both love and hate at
the same time), but which in general I think is an improvement over
the current format.

I'd like to get some comment about what others think about this
alternative format, and perhaps some justifications for the current
format (are there common cases where the current format makes sense
visually?) or confirmation that I really should be trying hard to
avoid multi-line conditionals in the first place.

David Symonds

unread,
May 21, 2012, 9:48:15 AM5/21/12
to wmpalmer, golang-nuts

I find that multiline conditionals are a good sign I should write a helper function or variable.

Peter Bourgon

unread,
May 21, 2012, 10:20:12 AM5/21/12
to wmpalmer, golang-nuts
> After a brief discussion in #go-nuts on freenode, the suggestion of an
> alternative formatting was made:
>
> if              conditionA &&
>                conditionB &&
>                conditionC {
>        bodyOfStatement()
> }

D:

> I find that multiline conditionals are a good sign
> I should write a helper function or variable.

+1

validFrob := conditionA && conditionB && conditionC
if validFrob {
bodyOfStatement()
}

Michael Jones

unread,
May 21, 2012, 10:39:07 AM5/21/12
to Peter Bourgon, wmpalmer, golang-nuts
suggestion:

if true &&
conditionA &&
conditionB &&
conditionC {
bodyOfStatement()
}

...there is an illustrious Unix, C, and Go developer whose personal
style is this...

if conditionA
if conditionB
if conditionC {
bodyOfStatement()
}

...I think of this "Thompson sequential conjunctive normal form",
unfortunately the mandatory use of braces in Go spoils the simple
beauty of TSCNF.
--
Michael T. Jones | Chief Technology Advocate  | m...@google.com |  +1
650-335-5765

Peter S

unread,
May 21, 2012, 8:17:41 PM5/21/12
to wmpalmer, golang-nuts
if conditionA &&
       conditionB &&
       conditionC {
       bodyOfStatement()
}
this has two major drawbacks in my opinion:
 
 2) there is no visual separation between the "conditions" and the
"body"

+1 for this, I find it visually confusing to have lines from different scopes aligned right under each other. This is also an issue when splitting a longish func declaration into two lines, and probably there are other similar cases as well. Add to this that the opening brace separating the scopes is mandatorily tucked away at the far side. (I actually do prefer opening braces on the right side in general, provided that indentation gives sufficient visual clue).

I know it is best to keep them short, but it is not always so easy (harder for func's than for if's), and multi-line makes it more readable in some cases (except for this issue).

I'd suggest to add an extra level of indentation for all multi-line statements/declarations, not only if's. Extra indentation would bring additional incentive for shortening when it is possible to do so, and less confusion (improved readability) when it is not.


 1) "conditionA" is visually separated from "conditionB" and
"conditionC"

I'm neutral about this one; unlike 2), I don't find it visually confusing, although it may be to considered to appear somewhat nicer. It could be argued that it is more consistent with the variable / comment alignment rules, but then there are probably also other places where adding extra mid-line whitespace could be considered for vertical alignment.

Just my two cents,

Peter

mvrak

unread,
May 23, 2012, 1:38:57 PM5/23/12
to golan...@googlegroups.com
Actually, I suggested the following:

if condition1() &&
condition2() &&
condition3() &&{
beginBody()
}

Kyle Lemons

unread,
May 23, 2012, 3:26:05 PM5/23/12
to mvrak, golan...@googlegroups.com
The "gofmt" style does not have an 80col limit.  If it gets past what you would consider "reasonable length" (for my project, 100-120 cols or so), I'm with david: I find it's often much more readable to break the conditional into boolean variables or a helper function.

mvrak

unread,
May 23, 2012, 3:32:31 PM5/23/12
to golang-nuts
Ok, ok, barring issues of readability, we arent specifically telling
people not to use multi-line conditions.

The question is whether go fmt could be making it more readable by
adding some small indentation difference between the extra lines and
the function body.

I see the merit there.

Saying that it goes against readability just means your are
ambivalent and it doesn't affect you.

Rodrigo Moraes

unread,
May 23, 2012, 8:42:57 PM5/23/12
to golang-nuts
On May 21, 5:56 am, wmpalmer wrote:
> After a brief discussion in #go-nuts on freenode, the suggestion of an
> alternative formatting was made:
>
> if              conditionA &&
>                 conditionB &&
>                 conditionC {
>         bodyOfStatement()
>
> }

That looks exotic to me. I like it the way it is. Honestly.

-- rodrigo

Kevin Ballard

unread,
May 23, 2012, 9:10:44 PM5/23/12
to Rodrigo Moraes, golang-nuts
Why not just indent the subsequent lines using 3 spaces instead of a tab, so all 3 conditionals line up?

if conditionA &&
   conditionB &&
   conditionC && {
bodyOfStatement()
}

-Kevin

Michael Jones

unread,
May 23, 2012, 10:19:40 PM5/23/12
to Kevin Ballard, Rodrigo Moraes, golang-nuts
Did you miss my suggestion upstream about "true" or just disagree with it?

Kevin Ballard

unread,
May 23, 2012, 11:54:56 PM5/23/12
to Michael Jones, Rodrigo Moraes, golang-nuts
Your suggestion upstream seems to be to insert a `true` as the first expression, so all the actual conditional expressions line up. But the real issue with the go fmt style is the conditionals line up with the body too, which means indentation cannot be used as a visual cue of where the body starts. The idea is to reintroduce indentation as a visual cue without making the conditional ugly.

I rather like

if conditionA &&
   conditionB &&
   conditionC {
bodyOfStatement()
}

for 3 reasons:

1. The conditionals all line up, including the one on the first line (with the `if`).
2. The conditionals have a lower indentation level than the body of the `then` clause. This makes it clear that they exist outside of the nested scope.
3. The usage of spaces for the subsequent lines in the conditional mean that they will line up regardless of the tab width of your editor.

-Kevin

Michael Jones

unread,
May 24, 2012, 1:03:24 AM5/24/12
to Kevin Ballard, Rodrigo Moraes, golang-nuts
Ah, I understand. Thanks.

Reynir Reynisson

unread,
May 24, 2012, 5:17:52 AM5/24/12
to Kevin Ballard, Rodrigo Moraes, golang-nuts
> Why not just indent the subsequent lines using 3 spaces instead of a tab, so all 3 conditionals line up?
>
> if conditionA &&
> conditionB &&
> conditionC && {
> bodyOfStatement()
> }
Some people might use a three space wide tabstop.

Norbert Roos

unread,
May 24, 2012, 11:23:01 AM5/24/12
to golan...@googlegroups.com
On 05/24/2012 11:17 AM, Reynir Reynisson wrote:

>> if conditionA&&
>> conditionB&&
>> conditionC&& {
>> bodyOfStatement()
>> }

> Some people might use a three space wide tabstop.

... which would result in

if conditionA&&
conditionB&&
conditionC&& {
bodyOfStatement()
}

Norbert

Paul Borman

unread,
May 24, 2012, 11:32:37 AM5/24/12
to Norbert Roos, golan...@googlegroups.com
Do forget other cases:

} else if A && B ... {

if statement; A && B ... {
}

And so on.  There coud be an entire heuristics paper on how best to format these things.  I agree the current gofmt solution is not optimal, however, having a single sub-optimal solution beats multiple "optimal" solutions.  With gofmt I know what to expect when reading code.   (And even BSD vs GNU go code would look the same with gofmt, oh the horrors!)

    -Paul

Norbert Roos

unread,
May 24, 2012, 11:35:06 AM5/24/12
to golan...@googlegroups.com
On 05/24/2012 05:32 PM, Paul Borman wrote:

> Do forget other cases:

It was just a note that setting tab to 3 doesn't solve the problem from
the OP..

Norbert

Kevin Ballard

unread,
May 24, 2012, 4:00:36 PM5/24/12
to Paul Borman, Reynir Reynisson, golan...@googlegroups.com
On Thursday, May 24, 2012 at 8:32 AM, Paul Borman wrote:
Do forget other cases:

} else if A && B ... {

if statement; A && B ... {
}

And so on.  There coud be an entire heuristics paper on how best to format these things.  I agree the current gofmt solution is not optimal, however, having a single sub-optimal solution beats multiple "optimal" solutions.  With gofmt I know what to expect when reading code.   (And even BSD vs GNU go code would look the same with gofmt, oh the horrors!)
The solution I'm proposing still fits here. Just one rule: "continued conditionals on subsequent lines are indented (with spaces) to match the start location of the conditional on the first line". This should cover all the cases.

On Thursday, May 24, 2012 at 2:17 AM, Reynir Reynisson wrote:

Some people might use a three space wide tabstop.
Then those people can live with having their conditionals visually line up with the body of their scope.

Either we use >1 tab (or tap + spaces) to indent subsequent lines, which looks awful for everybody, or we use a solution that looks good for nearly everybody but the occasional person with weird settings has a slightly sub-optimal experience (and honestly, their experience would just be the same as everybody else's is today).

-Kevin
 

Paul Borman

unread,
May 24, 2012, 4:09:22 PM5/24/12
to Kevin Ballard, Reynir Reynisson, golan...@googlegroups.com
So with something like:

<TAB>if value, err := something.Method(buffer, source); err == nil && value == 17 {
<TAB><TAB>code
<TAB>}

you would end up with

<TAB>if value, err := something.Method(buffer, source); err == nil && 
<TAB>                                                   value == 17 {
<TAB><TAB>code
<TAB>}

?

Kevin Ballard

unread,
May 24, 2012, 5:14:38 PM5/24/12
to Paul Borman, Reynir Reynisson, golan...@googlegroups.com
Yes, that's what I'm suggesting. It keeps the conditional visually grouped.

-Kevin

Kyle Lemons

unread,
May 24, 2012, 6:07:45 PM5/24/12
to Kevin Ballard, Paul Borman, Reynir Reynisson, golan...@googlegroups.com
On Thu, May 24, 2012 at 2:14 PM, Kevin Ballard <kbal...@gmail.com> wrote:
Yes, that's what I'm suggesting. It keeps the conditional visually grouped.

Please no.  I see that for function argument indentation for some languages, and it often ends up with about 10 characters of leeway *on every subseqent line*.

Kevin Ballard

unread,
May 24, 2012, 6:20:58 PM5/24/12
to Kyle Lemons, Paul Borman, Reynir Reynisson, golan...@googlegroups.com
On Thursday, May 24, 2012 at 3:07 PM, Kyle Lemons wrote:
On Thu, May 24, 2012 at 2:14 PM, Kevin Ballard <kbal...@gmail.com> wrote:
Yes, that's what I'm suggesting. It keeps the conditional visually grouped.

Please no.  I see that for function argument indentation for some languages, and it often ends up with about 10 characters of leeway *on every subseqent line*.

If you've got that many lines for one statement, then I think you have more wrong than just your indentation.

-Kevin

Kyle Lemons

unread,
May 24, 2012, 6:29:26 PM5/24/12
to Kevin Ballard, Paul Borman, Reynir Reynisson, golan...@googlegroups.com
On Thu, May 24, 2012 at 3:20 PM, Kevin Ballard <kbal...@gmail.com> wrote:
On Thursday, May 24, 2012 at 3:07 PM, Kyle Lemons wrote:
On Thu, May 24, 2012 at 2:14 PM, Kevin Ballard <kbal...@gmail.com> wrote:
Yes, that's what I'm suggesting. It keeps the conditional visually grouped.

Please no.  I see that for function argument indentation for some languages, and it often ends up with about 10 characters of leeway *on every subseqent line*.

If you've got that many lines for one statement, then I think you have more wrong than just your indentation.

My suggestion, stated earlier, is to not break conditionals at all, but to break it out into boolean variables or helper functions if that gets too long for you.  I don't think you can construct a simple heuristic for nice indentation of subsequent conditional lines no matter how hard you try.

reynir

unread,
May 24, 2012, 7:09:16 PM5/24/12
to golan...@googlegroups.com
I noticed the vim plugin indents it like the following, i. e. no extra indentation:


if conditionA &&
conditionB &&
conditionC {
        bodyOfStatement()
}

At first when I saw this I disliked it, but I think I prefer this over any of the other suggestions and the default.

I also noticed gofmt doesn't respect newlines after SimplStmt in an if statement. Example:

if ok := false; // gofmt removes this newline
false {
        bodyOfStatement()
}

PS. I'm sorry if the formatting is weird, I'm using the web interface this time.
Reply all
Reply to author
Forward
0 new messages