Declarative Syntax of Go Language Learning Course

  • 2020-06-12 09:20:11
  • OfStack

preface

Learning a new language are certainly starting from his basic grammar, syntax formed the basis of the overall program design, we can also see from the syntax 1 some characteristics of the language, but then again, in the grammar, different languages the same, so the memory of grammar also caused the difficulty of the 1 set, in fact, the best way should be next to a book, can always bring them here to consult the or corrected.

Beginners of Go may wonder why Go's declarative syntax is not quite the same as other traditional C family programming languages. In this article we will compare the two approaches and explain why. Without further ado, let's take a look at the details.

C variable

First, let's talk about the syntax in C. C USES an unusually clever approach to implementing declarative syntax. Instead of using a special syntax to describe a type, we write an expression that has two parts: the declared variable and the type of the variable.


int x;

The above line declares a variable of type int, x. In general, to figure out how to write the type of a new variable, you can first write an expression with a primitive type variable, then place the primitive type on the left and the expression on the right.

Therefore, the following statement:


int *p;
int a[3];

Describes that p is a pointer to the type int, since the type '*p' is int. a is an array of type int, because 'a[3]' (ignore the subscript value 3 here, it just indicates the size of the array) is of type int.

What about the function? In the beginning, C's function declaration was to write the type of the argument outside the parentheses, like this:


int main(argc, argv)
 int argc;
 char *argv[];
{ /* ... */ }

Again, we can see that main is a function because the expression main(argc, argv) returns a value of type int. Now, the usual way to write it is this:


int main(int argc, char *argv[]) { /* ... */ }

But the basic structure is the same.

This clever syntax works well for simple types, but once a type becomes more complex it can be confusing. A classic example is declaring a function pointer. Following the rules, you get the following:


int (*fp)(int a, int b);

fp is a pointer to a function, because if you write an expression (*fp)(a, b) you will call the function and get a value of type int. What if one of the fp entries is itself a function?


int (*fp)(int (*ff)(int x, int y), int b)

This becomes difficult to read.

Of course, the main function can be declared as:


int main(int, char *[])

Let's recall that argv declares,


char *agrv[]

It is actually confusing to declare a type like char *[] by putting the variable name in the middle.

Then let's look at what the fp function declaration would look like if we took out the name of the parameter:


int (*fp)(int (*)(int, int), int)

No matter where you put the variable name inside, it's not that clear. For the first input:


int (*)(int, int)

I don't think it's easy to see that 1 is declaring a pointer to a function. One more step, what if our return value is also a function pointer?


int *p;
int a[3];
0

It's not clear what the claimed fp is...

You could construct more detailed examples of this type yourself, but these illustrate some of the difficulties that C's declarative syntax can introduce.

But there's one more point that Needs to be made. Because the type and declaration syntax are the same, parsing an expression of an intermediate type is difficult. This is why C's cast is always enclosed in parentheses:


(int)M_PI

Go grammar

Programming languages that are not part of the C family typically use a different syntax for declaration types: the variable name is usually preceded by a colon. So our example above looks like this:


x: int
p: pointer to int
a: array[3] of int

These statements are explicit and, if read from left to right, detailed. The Go language takes a cue from this, but for brevity, the colon and 1 are removed:


int *p;
int a[3];
3

There does not seem to be a direct correspondence between [3]int and how to use a in an expression in this example. (We'll talk about Pointers in the next section.) You can get clear results with separate syntax.

Now let's think about functions. Let's write this declaration as Go, even though the real main function in Go has no arguments:


int *p;
int a[3];
4

On the surface, this is no different from the C language, except that it changes the character array to a string. But it reads smoothly from left to right:

The main function needs to pass in an integer and a string slice and return an integer. (Note: until the translator saw this article, it was so easy to write and read...)

Even dropping the variable names is clear -- there is no confusion because there is no change of position on the type declaration.


int *p;
int a[3];
5

This left-to-right style has one advantage: it works well even as the types become more complex.

An example of declaring a function variable (similar to function Pointers in C) :


int *p;
int a[3];
6

Or if f returns 1 function as well. :


f func(func(int, int) int, int) func(int, int) int

It still reads smoothly from left to right and is obvious when variable names are declared.

Differences in the syntax of types and expressions make it easy to write and invoke closures in Go:


sum := func(a, b int) int { return a + b } (3, 4)

Pointer to the

This guy is always acting "different" at 1. Looking at arrays and slices, for example, Go's type syntax puts square brackets to the left of the type, but assignment expression syntax puts them to the right of the expression:


int *p;
int a[3];
9

To give you a sense of familiarity, the pointer to Go also continues the * symbol in C, but we cannot simply reverse the pointer type by 1 as well. So the pointer is used as follows:


var p *int
x = *p

We can't simply put it this way:


var p *int
x = p*

Because suffixes are confused with multiplication. So maybe we could use the ^, for example:


var p ^int
x = p^

But the same notation has taken on other meanings, and types and expressions always complicate things in many ways with prefixes and suffixes. For example,


[]int("hi")

This is one way to write it, but once a * begins, it must be enclosed in parentheses:


(*int)(nil)

These parentheses would not be necessary if we were willing to give up * as a pointer syntax. But could there be a better pointer syntax...

So the pointer syntax of Go is similar to that of the familiar C language, but this association also means that we have to use parentheses to eliminate differences between types and expressions in the syntax.

In general, we believe that Go's type syntax is easier to understand than C's, especially when things get complicated.

Why does the Go language use this reverse syntax?

Rob Pike, the designer of ES18180en, gives the answer in an article introducing the Go declarative grammar, which discusses the design considerations of the Go declarative grammar.

conclusion


Related articles: