Why is Go's declaration syntax "backward"? (The Clockwise/Spiral Rule)

Spirals!

They exist throughout the universe, from the smallest seashells to the largest galaxies.

They exist in mathematics and arts.

The neolithic monument of Newgrange, Ireland, is 5,200 years old—older than Stonehenge or the Egyptian pyramids. One remarkable feature is the big entrance stone that carries beautiful spiral engravings.

If you enjoyed the book "The Good Spy Guide: Secret Messages" by Falcon Travis and Judy Hindley in your youth, then (a) you are probably as old as I am, and (b) have learned, and maybe even used, the spiral cipher for encrypting and decrypting secret messages.


I don't know whether the designers of the C language knew this book, too, or whether they unintentionally have introduced spirals into the language. But they do exist, and they play a vital role in reading declarations.

C-style declarations: spiral your way out

On 1994-05-06, exactly 29 years before this article was published, David Anderson posted an elaborate explanation of how to read C declarations to the Usenet forum comp.lang.c. The gist of David's post was a technique called the "Clockwise/Spiral Rule" that enables the avid C developer to decipher C declarations of any complexity. 

Here is a first, simple example, right from the original post. Consider this C declaration:

char *str[10];

What does this line declare? To answer this, you start at the name of the declared entity and spiral clockwise outwards, like so:

     +-------+
     | +-+   |
     | ^ |   |
char *str[10];
 ^   ^   |   |
 |   +---+   |
 +-----------+

(All spiral diagrams are taken from the original post.)

So you start at str, move on to [10] (an array of length 10), further on to * (denoting a pointer type), and finally to char.

Now you know that "str is an array of length 10 of pointers to char".

This rule is genius! No C declaration can hide its true meaning anymore!

Occasionally, you'd need to read a spiral inside a spiral, as in this example:

void (*signal(int, void (*fp)(int)))(int);

Starting at signal, you quickly discover (by following the spiral rule) that this is a function with an int as the first parameter, and some gibberish as the second parameter. Here, you can start a second spiral starting at fp and decipher that parameter before returning to the main spiral.

Your journey through the declaration looks like this:

      +-----------------------------+
      |                  +---+      |
      |  +---+           |+-+|      |
      |  ^   |           |^ ||      |
void (*signal(int, void (*fp)(int)))(int);
 ^    ^      |      ^    ^  ||      |
 |    +------+      |    +--+|      |
 |                  +--------+      |
 +----------------------------------+

(Find the complete explanation in the original post, Example #3. I have skipped Example #2 for brevity.)

At this point, you can tell that "signal is a function that passes an int and a pointer to a function named fp passing an int returning nothing and returns a pointer to a function passing an int returning nothing."

Piece of cake!

Unfortunately, the designers of Go did not carry that gem of language design culture over to their language.

Go-style declarations: boooring

Go is known as The Boring Language, and so are its declarations.

Here is the rule you have to follow: Read them from left to right.

That's it.

If you don't believe me, here is the C example #1 in Go syntax. The letters in the comment mark the individual parts of the declaration:

var str [10]*string
//a b   c   de

Read: "A variable (a) named str (b) that is an array of length 10 (c) of type pointer (d) to string (e)."

Do not expect anything more exciting for the Go version of the C example #3:

func signal(sig int, fp *func(int)) *func(int)
// a b      c   d    e  fg    h     ij    k

Read: "A function (a) named signal (b) with a parameter named sig (c) of type int (d) and a second parameter fp (e) of type 'pointer (f) to a function (g) that takes an int (h) and no return value' that returns a pointer (i) to a function (j) taking an int (k) and returning nothing.

Again, the declaration reads straight from left to right. No exceptions.

It's all about readability

Still awake?

Yes, I know, Go declarations are not nearly as exciting as C declarations.

But the Go designers did not accidentally derive from the good ol' C style. Turning the declarations around by 180 degrees (n int instead of int n) makes all the difference in reading and understanding a declaration with confidence.

Go is, among other features, a language made for readability. Code is much more often read than written. Hence, a productivity-oriented programming language must avoid at all cost being a write-only language that encourages clever coding at the expense of readability.

The Go designers have done an outstanding job in this regard.

And now pull the plug of your kitchen sink and watch the water spiral out.

Disclaimer: No AI was harmed in the making of this article.

Categories: The Language