0

My understanding is that go fmt is supposed to produce code that is readable and consistently formatted. However I'm not finding this to be the case.

I typed in the commented code and go fmt returned the uncommented code. Why did it collapse 0.5*(y3-y0), but not 0.5 * (y2 - y0) ? How is that consistent? And, IMO, the return line with nearly every space collapsed out is a readability disaster.

Is the inconsistency a bug? Is there a way to get go fmt to leave some lines (like the return line) alone?

func cubicInterpolate(x, y0, y1, y2, y3 float64) float64 {
    // 4-point, 3rd-order Hermite (x-form)
    // c0 := y1
    // c1 := 0.5 * (y2 - y0)
    // c2 := y0 - 2.5 * y1 + 2. * y2 - 0.5 * y3
    // c3 := 1.5 * (y1 - y2) + 0.5 * (y3 - y0)
    //
    // return ((c3 * x + c2) * x + c1) * x + c0

    c0 := y1
    c1 := 0.5 * (y2 - y0)
    c2 := y0 - 2.5*y1 + 2.*y2 - 0.5*y3
    c3 := 1.5*(y1-y2) + 0.5*(y3-y0)

    return ((c3*x+c2)*x+c1)*x + c0
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
asynth
  • 214
  • 2
  • 8
  • 5
    Gofmt's style is no one's favorite, yet gofmt is everyone's favorite. –  Nov 24 '19 at 22:59
  • 2
    get used to it. https://go-proverbs.github.io/ –  Nov 24 '19 at 23:00
  • I have a similar problem where gofmt is changing indentation of a structured comment that's consumed by `swagger` from spaces to tabs, resulting in an obscure swagger error: `json: cannot unmarshal string into Go value of type spec.SecuritySchemeProps` – Ed Randall Apr 14 '23 at 10:53

1 Answers1

3

This is documented in the go source code; the aim is to make operator precedence clear. For example in your example y0 - 2.5*y1 + 2.*y2 - 0.5*y3 the multiplications will be performed before the subtractions and the format chosen makes this clear at a glance.

In answer to your question; this is not a bug; indeed quite a bit of effort was put into the formatting. You cannot exclude a line from formatting; this is by design, as mentioned in the FAQ, the aim is to enforce layont rules:

gofmt is a pretty-printer whose purpose is to enforce layout rules; it replaces the usual compendium of do's and don'ts that allows interpretation.

Below is a formatted extract from go/printer/nodes.go that details the formatting rules:

Format the binary expression: decide the cutoff and then format. Let's call depth == 1 Normal mode, and depth > 1 Compact mode. (Algorithm suggestion by Russ Cox.)

The precedences are: 5 * / % << >> & &^ 4 + - | ^ 3 == != < <= > >= 2 && 1 ||

The only decision is whether there will be spaces around levels 4 and 5. There are never spaces at level 6 (unary), and always spaces at levels 3 and below.

To choose the cutoff, look at the whole expression but excluding primary expressions (function calls, parenthesized exprs), and apply these rules:

  1. If there is a binary operator with a right side unary operand that would clash without a space, the cutoff must be (in order):
   /* 6   
   && 6   
   &^ 6   
   ++ 5   
   -- 5 

(Comparison operators always have spaces around them.)

  1. If there is a mix of level 5 and level 4 operators, then the cutoff is 5 (use spaces to distinguish precedence) in Normal mode and 4 (never use spaces) in Compact mode.

  2. If there are no level 4 operators or no level 5 operators, then the cutoff is 6 (always use spaces) in Normal mode and 4 (never use spaces) in Compact mode.

Brits
  • 14,829
  • 2
  • 18
  • 31
  • I didn't have a problem with ```y0 - 2.5*y1 + 2.*y2 - 0.5*y3```. It is that ```0.5 * (y2 - y0)``` and ```0.5*(y3-y0)``` are formatted differently from each other despite being the same, and that there is nothing clear at a glance in this: ```((c3*x+c2)*x+c1)*x + c0```. – asynth Nov 25 '19 at 04:54
  • @asynth: the `0.5 * (y2 - y0)` has spaces in it because it is the entire expression. The `0.5*(y3-y0)` has no spaces because it's *not* the entire expression: it is the RHS of a sum. That's really all there is to it. (BTW I agree with you that the `return` layout is particularly terrible, but I don't get to choose here.) – torek Nov 25 '19 at 05:26