Wang Yin: comprehensive evaluation of the Go language


Previously written some negative comments on the Go language. It now appears that, although most of those are true evaluation, yet due to the strong words, no pointing to specific issues, making it difficult for some people to convince. After months of actual use Go to construct the site, I think now it is time to make some more "objective" evaluated.

1. Locate and advantages

Go compared to C and C ++ does have its advantages, it is very obvious thing. It also has a few advantages compared to Java, but is relatively more deficiencies. So in the position I prefer to Go slightly lower than Java.

Go language than C, C ++ strengths, of course, is its simplicity and garbage collection. Since C and C ++ design has a lot of historical issues, so Go does look more elegant and simple. Than those who use a lot of Java code design patterns, Go language code seems simpler. In addition, Go's garbage collection compared to C and C ++ full manual memory management, it greatly reduces the burden on the programmer's mind.

Note, however, where the so-called "advantages" are relative to the C language such terms. If some of the language than the other, this advantage may very insignificant Go, even historical retrogression.

2. Grammar

Go simplicity is reflected in certain aspects of its syntax and semantics. Go's syntax is slightly better than the C number, there are a few more convenient than Java design, but it is also a "retrogression" in place. And these are not a lot of people back as retro, but that is progress. I mentioned being able to want to get up several aspects:

  • Progress: Go supports a syntax similar structure struct literal, for example you can write code to construct a S struct:
S { x: 1, y: 2, }

This can only use Java constructor to create an object than an improvement on a nice convenience. These things may learn to design languages ​​such as JavaScript.

  • Reverse: variable type on the back, but no delimiter. If the variable and its type is written like Pascal as, for example, x: int, then maybe okay. However, Go wording is x int, not the colon, but also allows the use of x, y int such wording. Var with this syntax, then the function parameters are combined, it produces the effect of distracting. For example, you can write a function like this beginning:
func foo(s string, x, y, z int, c bool) {
  ...
}

Note that x, y, z position that, in fact, is very confused. Because when I saw the x can not immediately see from behind the symbols (, y) what type it is. So I recommend wording is completely separate from the x and y Go in there, just like C and Java, but the type written on the back:

  func foo(s string, x int, y int, z int, c bool) {
    ...
  }

As a relatively clear, although I would like to write some more colon. Each parameter is "the name of the type" format, so I look to see that x is int. Although the play more words, but the savings are "eyeball parse the code" costs.

  • Back: type syntax. Go uses like [] string This syntax to indicate the type. Many people say that this syntax is very "consistent", but after a while I did not find out where they called consistency. In fact, this syntax is hard to read, because there is no clear separation between the various parts of the type of identifier, and if the other symbols, such as * mix together, you need to know some of the priority rules and fees relatively large effort to do "eye parse". For example, in the Go code you often see [] * Struct this type, note * Struct together first, and then as [] "Type parameter." This lack of adequate delimiter syntax as a "boundary signal" to read, once the latter type is complicated, it is difficult to read. For example, you can have * [] * Struct or * [] This type pkg.Struct. So this is actually not as good as C ++, the Vector <struct > such wording, it is even more not as good as Java or Typed Racket type of writing more clear and simple.

  • Back: excessive "syntax overload", such as switch, for keywords and so on. Go's switch key actually contains two different things. It may be an ordinary switch C inside (Scheme's case), it may be like that for Scheme cond nested branch statements. These two statements are in fact quite different semantics, but Go designers seem to simple, put them into one, but in fact cause greater confusion. This is because even if you put them into one, they are still two different semantic structure. Merge them a result, every time you need to switch from their "head" of different points of the structure of these different two separate zones, increasing the cost of the human brain. The correct approach is to separate them, just as Scheme. In fact, when I design language sometimes make the same mistake, the two things that the "essence" is the same, so combined, the result over time, discovered that in fact is not the same. So do not underestimate Scheme, you think a lot of "new ideas" thing, in fact, that it would have been very strict in the Commission to abandon the course of history.

Go inside there are other language grammar design issues, such as {after the forced line breaks in a row and can not, at the beginning of the judgment can be nested if statements assignment and so on. These programs appear to be trying to make short practice, in fact, but reduces the fluency program understand.

So All in all, Go's syntax is very difficult to be called "simple" or "elegant", its simplicity is actually under Java.

3. toolchain

Go offers some convenient tools. For example gofmt, godef the like, so that the program code than Go alone or VIM Emacs to edit C and C ++ is an improvement. Go using the Emacs editor has been able to achieve some have IDE features such as precise definition of jump and so on.

Although these tools are easy to use, but compared to like Eclipse, IntelliJ, and Visual Studio such as IDE, the gap is still considerable. Compared to IDE, Go tool chain lack the most basic functions of all kinds, such as listing all references to the location of a variable, rename refactor function, easy to use debugger (GDB is not easy to use) and so on.

Go various tools are not feeling great mature, sometimes you find that there are several different package to solve the same problem, do not know which is better. And these things up configuration is not so reliable and simple, we need to toss. Every little feature you have to come from everywhere to look for package configuration. Sometimes after the fact, a configuration tool does not work, you have to wait quite a while to find the problem and explore where. This is no organization, no planning tool design, it is difficult to exceed the continuity of professional IDE vendors.

Go package provides a convenient mechanism to directly import a GitHub repository in the Go code. But I found a lot of time to bring this package mechanism is more trouble and dependencies. So Go proponents who has designed a number of tools such as godep used to circumvent these problems, the results godep themselves cause some strange problems, sometimes leading to the new code is not compiled in fact, an error message inexplicable ( godep may be due to the bug).

I found that a lot of people, after seeing these tools are always very fanatical think they can make the Go language to dominate the world, in fact, worse very far. And such a young language had already seen so many problems, I think that all these troubles accumulated, many years later, I am afraid unbearable.

4. Memory Management

Compared to C and C ++ fully manual memory management, Go has garbage collection (GC) mechanism. This mechanism greatly reduces the burden on the minds of opportunity and programmers error, so Go for C / C ++ is a step forward.

However, progress is relative. Go's garbage collector is a very original mark-and-sweep, which compared to languages ​​like Java, OCaml and Chez Scheme and the like to achieve, in fact, still in its infancy.

Of course, if you really GC performance issues encountered by a lot of tuning, you can improve the efficiency of memory recovery section. I also saw someone wrote some articles describe how they do these things, but the existence of this article describes the Go's garbage collection is still very immature. GC I think most of the time this kind of thing should not allow the programmer to worry about, otherwise we lose the GC lot of advantages compared to manual management. So Go in real-time code that you want more high occasion, still has a long way to go.

Due to the lack of advanced GC, but with a higher level of abstraction, so Go can not actually replace the C and C ++ to construct the underlying system. Go language localization for me more and more blurred.

5. There is no "generics"

Compared to C ++ and Java for, Go the lack of generics. Although some people hate the Java generics, but it itself is not a bad thing. Generics in fact, Haskell and other functional languages ​​inside the so-called parametric polymorphism, is a very useful thing, but after being copied to the Java sometimes not done full right. Because generics allows you to handle a variety of different data types with the same piece of code, it is to avoid duplication, to facilitate the replacement of complex data structure provides a convenient.

Since the Go no generics, so you have to write a lot of repeat function, each only a different type. Or you can use an empty interface {}, but this thing is in fact equivalent to C's void * pointer. After using it, the code can not be static type checking, so in fact it does not have to generics rigor.

Compared to Java, many data structures of Go are "hard code" into the language which, even created special keywords and syntax to construct them (such as a hash table). Users define their own needs in the event of similar data structure, it is necessary to rewrite a lot of code. And because there is no similar Java collections of things, you can not easily replace complex data structures. This structure such as a large number of experiments required to select the correct data structure like PySonar, need to implement a special program hash tables and other data structures, these deletions Go language would be a very big obstacle.

Lack of generics is a problem, however, is a more serious problem Go designers and their communities for this type of language features blind rejection. When you mention these, Go supporters will be in a contemptuous attitude to tell you: "I do not see what use generics!" This attitude is more harmful than the shortcomings of the language itself is. After a long period of time Go language designers began to consider joining the generics , and because the syntax design Go cut corners, exceptions (such as syntax design map of Go's) coupled with the lack of generics produced has been widely used I feel the need to join the difficulty of generics has been very large.

Go and Unix systems, at an early stage because they do not appear to have learned the lessons of their predecessors, carry a heavy historical burden.

6. Multi return value

Many people feel more return value Go design is a step forward, but there there are many strange things. Not to mention that this is not anything new (Scheme has long had more than the return value of let-values), Go's been a lot more than the return value is used in the wrong place -Go using multiple return values ​​to represent error message. For example, the Go is the most common structure:

ret, err := foo(x, y, z)
if err != nil {
	return err
}

If you call foo generates an error, then err is not nil. Go requires you to use it after the definition of a variable, otherwise an error. So that it "happened" to avoid a situation without checking the error err. Otherwise, if you want to ignore the error, it must be written

ret, _ := foo(x, y, z)

So that when foo wrong, the program will automatically in that position when off.

Have to say, this "lucky hit" approach although seemingly feasible, from the perspective of the type of system, but it is not very rigorous. Because it is not designed for this purpose, so you can come up with easier ways to make it fail. And because the compiler only checks whether err is "use", check if you do not check the "all" types of errors that can occur. After only use the data, for example, if foo might return two errors Error1 and Error2, you can not completely rule out the caller to ensure that both the possibility of error. So this error checking mechanism is still not as the exception to the rigorous Java.

In addition, ret err, and at the same time be defined, and each only one not nil, this "or" relationship not by the compiler to protect, but by programmers' convention. " Err so that when the time is not nil, ret in fact may not be nil. These combinations bring a lot of confusion, so that every time you see the return of places are not convinced that in the end want to return an error or a valid value. If you are aware of this, "or" relationship actually means that you should only use a return value to represent them, in fact, you know Go misuse multiple return values ​​to indicate possible errors.

In fact, if a language has like Typed Racket and PySonar supported "union type" type system, this multi return value does not make sense. Because if you have a union type, you can only be expressed valid data or an error with a return value. For example, you could write a type called {String, FileNotFound}, is used to represent a value of either String, either FileNotFound error. If you have to use a function may return after data error, the compiler will force the programmer to check all possible errors, which can completely avoid confusion over the various cases. Interested people can look for the union type Typed Racket, it has the most powerful I've ever seen the type of system (beyond the Haskell).

So it can be said that this multi-Go's return value, is actually a "lucky hit" the name of the half, and then for another way to continue a lucky hit, rather than aim at the bull's-eye.

7. Interface

Go uses object-oriented design of the interface (interface) based, you can use the interface to express abstract concepts want to.

However, this interface design is not no problem. First with Java, that implements the interface does not need to explicitly declare (implements) a Go, so you might have "accidentally" implements an interface. This uncertainty in understanding the program is counterproductive. Sometimes after you modify a function not found by the compiler, one need not complain of a position transfer interface, but an error message but can not tell you the exact reason. You have to go through some exploration discovered a previously defined interface to why your struct no longer achieve.

In addition, some people use the interface, but a lot of time to pass some function as a parameter. I sometimes do not understand this simple enough thing to functional language, in which language Why Go To define an interface to another realization. This makes the program not as a functional language so clear, but it is also very easy to modify. There are a lot of redundant names to be defined, redundant work to do.

A case in point is the Go For the Sort function . Each requires some sort an array of type T, such as [] string, you need to

  • Further definition of a type, generally called a TSorter, such StringSorter
  • This type definition is StringSorter three methods are called Len, Swap, Less
  • Put your type, such as [] string cast into StringSorter
  • Call sort.Sort sort this array

Think about the sort of functional languages ​​in how simple, right? For example, Scheme and OCaml can be directly written:

(sort '(3 4 1 2) <)

Here the function of Scheme <sort function as an argument to direct, without any interfaces in the package inside. You find it, there are three methods that interface Go, in fact, should have been as three parameters passed directly to Sort, but due to the limitations of thinking design pattern, etc., Go designers put their "package" as an interface to transfer . Go and because no generics, you can not write like a functional language, like these three functions, accept the comparison of "elements" as a parameter, but must use their "index." Since these methods only accepted index as a parameter, so that only Sort sort the array. In addition, as Go design is more "bottom", so you need two additional parameters: len and swap.

In fact, this fact than functional languages, is a big gap interface-based design. Compared to Java interface design, it can be said to be a step backwards.

8.goroutine

Goroutine arguably the most important feature of Go. Many people use the Go is heard goroutine to support the so-called "big concurrency."

This is not the first big concurrency nothing new. Everyone understand programming language theory knows goroutine fact, some of the user-level "continuation". System-level continuation is often called "process" or "thread." Continuation is a functional language, but experts find out about things, like my former mentor Amr Sabry is one of the top experts on continuation.

Node.js that "callback hell", which is actually functional languages ​​commonly used technique, called continuation passing style (CPS). Since the Scheme have call / cc, so in theory, it can not be achieved through a large concurrent CPS-style code. So long as the functional language support continuation, it will be very easy to achieve high concurrency, and perhaps more efficient, better use some. Scheme implementation of such a Gambit-C can be used to implement concurrent large things. Chez Scheme may also be, but yet to be confirmed.

Of course, the efficiency of specific implementation may differ, but I'm just saying, goroutine in fact, is not as new, revolutionary, unique things many people imagine. As long as there is enough power, other languages ​​can add this thing.

9.defer

Go achieved defer function, after the function for avoiding an error forgotten clean up the mess (cleanup). However, I found that this function has a tendency to defer abuse. For example, some people are not the kind of cleanup actions also made defer, to accumulate later after several defer, you can no longer see a clear run after piece of code running in the end piece of the first. Position in the preceding code actually can run later in violation of the natural order of relationships location code.

Of course, this can blame programmers do not understand the true purpose defer, but once you have this kind of thing people would want to abuse it. Kind of a rush trying to use the language of each feature of people, especially like doing this kind of thing. After this problem will probably take many years of experience, someone will write a book to educate everyone. In the formation of a unified "code specifications" Before, I predict defer will still be a lot of abuse.

So we should think about in order to avoid resource leaks that may occur, defer to bring in the end is more or more disadvantages than benefits.

10. The library code

Go standard library design inside with a strong flavor of Unix. Compared language like Java, it has a lot of library code inconvenient place. Sometimes we introduce some ways functional languages, but due to the limitations of Unix thinking, not only failed to play to the advantages of functional languages, but also led to a lot of understanding of the complexity.

Go is an example of the character string processing. Included in Java each character in the string, the default is Unicode's "code point". However, each element is a byte string type in Go inside it, so every time you have to cast it into "rune" can correct type of walk through each character, then cast it back. This put anything as byte way is Unix's way of thinking, it is the underlying cause excessive and complex code.

11.HTML template library

I used Go's template library to generate some pages. This is an "essential use" template mode, but compared to many other mature technology, but it is quite inadequate. So I am more surprised that, Go entrained inside the template code is not actually own the Go language, but a language ability is quite weak, a bit like a degenerative of Lisp, but replaced the brackets {{... }} such thing.

For example, you can write a page template:

{ {define "Contents"} }
{ {if .Paragraph.Length} }
<p>{ {.Paragraph.Content} }</p>
{ {end} }
{ {end} }

Since each template accepts a struct as populated data, you can use .Paragraph.Content such code, but this is not only ugly, but let the template is not flexible, difficult to understand. You need to put all the required data into the same structure templates can access them from the inside.

Any more than one line of code, although perhaps this language can express, the average person in order to avoid the weaknesses of this language, or write some "help function" in .go file inside. Generating a data structure into with them, and then passed to the template, the template is able to express some of the information needed. And that each function and need some help, "registration" information can be found template library. So these complexities add up to make the HTML template code Go considerable trouble and confusion.

I heard someone make a new HTML template system can support direct Go code embedded. The work has just started, but hard to say what will eventually be made. So do the site, probably still best to use other languages ​​more mature framework.

12. summary

Elegance and simplicity is relative. Although the Go language in many ways more than the C and C ++, also in some respects better than Java, but it is actually impossible and elegance compared to Python, and Python but not as good as Scheme and Haskell in many ways. So All in all, Go degree of simplicity and elegance belong to the lower middle.

Since there is no obvious advantage, but there are a variety of other languages ​​there are no problems, so in the actual project, I currently prefer to use a language like Java. I do not think the Go language and its tool chain to help me write quickly as sophisticated PySonar code. In addition, I also heard someone use Java to achieve high concurrency, compared to Go did not find any significant deficiencies.

Alan Perlis said that language should not be designed to function piled up, but should strive to reduce vulnerabilities. From this point of view, Go language to introduce one or two new features, but also introduces considerable weaknesses.

Go perhaps temporarily in some individual cases with special strengths, it can be used in some parts of the optimization of the system alone, but I do not recommend using Go to implement complex algorithms and the entire system.

Blog Source: http: //www.yinwang.org/blog-cn/2014/04/18/golang

Published 46 original articles · won praise 27 · views 160 000 +

Guess you like

Origin blog.csdn.net/shichen2010/article/details/104553828