The article is written as a response to a previously published antipode article .
Over the past two years, I have been using Go to implement a specialized RADIUS server with an advanced billing system. In the course of studying the subtleties of the language itself. Programs themselves are very simple and are not the goal of writing an article, but the experience of using Go deserves to say a few words in its defense. Go is becoming an increasingly popular language for serious scalable code. The language was created in Google, in which it is actively used. Having drawn the line, I sincerely believe that the design of the Go language is bad for non-intelligent programmers.
The weak talk about problems. Strong talk about ideas and dreams ...
Go is very easy to learn, so simple that you can read the code with little or no training at all. This feature of the language is used in many global companies when the code is read together with non-core specialists (managers, customers, etc.). This is very convenient for methodologies such as Design Driven Development.
Even novice programmers begin to issue quite a decent code after a week or two. The book on which I studied Go is called “Programming in Go” (written by Mark Summerfield). The book is very good, it affects many of the nuances of the language. After unreasonably complicated languages such as Java, PHP, the lack of magic acts refreshingly. But sooner or later, many limited programmers have a desire to use the old methods in the new field. Is it really necessary?
Rob Pike (the main ideologue of the language) created the Go language, as an industrial language that is easy to understand, effective in use. The language is designed for maximum productivity in large teams and there is no doubt about it. Many novice programmers complain that there are many features that they lack. This striving for simplicity was a conscious decision of the developers of the language and, in order to fully understand why this was necessary, we must understand the motivation of the developers and what they sought in Go.
So why was it created so simple? Here are a couple of Rob Pike quotes:
The key point here is that our programmers are not researchers. As a rule, they are very young, they come to us after their studies, they have probably studied Java, or C / C ++, or Python. They are not able to understand the outstanding language, but at the same time we want them to create good software. That is why language should be easy to understand and learn.
He should be familiar, roughly speaking similar to C. Google programmers start their careers early and most of them are familiar with procedural languages, in particular the C family. The demand for fast productivity in a new programming language means that the language should not be too radical.
Wise words, isn't it?
Simplicity is a prerequisite for beauty. Lev Tolstoy.
Being simple is one of the most important aspirations in any design. As you know, a perfect project is not a project where there is nothing to add, but one in which there is nothing to remove. Many believe that in order to solve (or even express) complex problems, a complex tool is needed. However, it is not. Take for example the language PERL. The ideologists of the language believed that the programmer should have at least three different ways to solve one problem. The ideologists of the Go language went the other way, they decided that to achieve the goal one way is enough, but really good. Such an approach has a serious foundation: the only way is easier to learn and harder to forget.
Many migrants complain that the language does not contain elegant abstractions. Yes, it is, but this is one of the main advantages of the language. The language contains in its composition a minimum of magic - therefore, deep knowledge is not required for reading the program. As for the verbosity of the code, this is not a problem at all. A well-written program in the Golang language is readable vertically, with almost no structuring. In addition, the speed of reading the program at least an order of magnitude greater than the speed of writing. If we consider that all the code has uniform formatting (done with the built-in gofmt command), then reading a few extra lines is not a problem at all.
Art does not tolerate when its freedom is constrained. Accuracy is not his responsibility.
Because of the desire for simplicity in Go, there are no constructions that are perceived in other languages as something natural by people who are used to them. At first this may be somewhat inconvenient, but then you notice that the program is read at times simpler and more unambiguous.
For example, a console utility that reads stdin or a file from the command line arguments will look like this:
package main import ( "bufio" "flag" "fmt" "log" "os" ) func main() { flag.Parse() scanner := newScanner(flag.Args()) var text string for scanner.Scan() { text += scanner.Text() } if err := scanner.Err(); err != nil { log.Fatal(err) } fmt.Println(text) } func newScanner(flags []string) *bufio.Scanner { if len(flags) == 0 { return bufio.NewScanner(os.Stdin) } file, err := os.Open(flags[0]) if err != nil { log.Fatal(err) } return bufio.NewScanner(file) }
The solution of the same problem in the D language, although it looks somewhat shorter, however, is not read at all easier.
import std.stdio, std.array, std.conv; void main(string[] args) { try { auto source = args.length > 1 ? File(args[1], "r") : stdin; auto text = source.byLine.join.to!(string); writeln(text); } catch (Exception ex) { writeln(ex.msg); } }
The man wears hell in himself. Martin luther
Newbies constantly complain about Go in terms of lack of generics. To resolve this issue, most of them use direct copying code. For example, such pseudo-professionals believe that the function for summing the list of integers is that it is impossible to implement the functionality in any way other than by simple copying and pasting for each data type.
package main import "fmt" func int64Sum(list []int64) (uint64) { var result int64 = 0 for x := 0; x < len(list); x++ { result += list[x] } return uint64(result) } func int32Sum(list []int32) (uint64) { var result int32 = 0 for x := 0; x < len(list); x++ { result += list[x] } return uint64(result) } func main() { list32 := []int32{1, 2, 3, 4, 5} list64 := []int64{1, 2, 3, 4, 5} fmt.Println(int32Sum(list32)) fmt.Println(int64Sum(list64)) }
There are sufficient means in the language to implement such constructions. For example, generic programming is fine.
package main import "fmt" func Eval32(list []int32, fn func(a, b int32)int32) int32 { var res int32 for _, val := range list { res = fn(res, val) } return res } func int32Add(a, b int32) int32 { return a + b } func int32Sub(a, b int32) int32 { return a - b } func Eval64(list []int64, fn func(a, b int64)int64) int64 { var res int64 for _, val := range list { res = fn(res, val) } return res } func int64Add(a, b int64) int64 { return a + b } func int64Sub(a, b int64) int64 { return a - b } func main() { list32 := []int32{1, 2, 3, 4, 5} list64 := []int64{1, 2, 3, 4, 5} fmt.Println(Eval32(list32, int32Add)) fmt.Println(Eval64(list64, int64Add)) fmt.Println(Eval64(list64, int64Sub)) }
And, although our code turned out to be somewhat longer than the previous case, but it became generalized. Therefore, we will not be difficult to implement all the arithmetic operations.
Many will say that a program in D looks much shorter and will be right.
import std.stdio; import std.algorithm; void main(string[] args) { [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln; }
However, only shorter, but not correct, since the implementation on D completely ignores the problem of error handling.
In real life, when the complexity of logic increases, the gap is rapidly narrowing. Even more rapidly, the gap is reduced when an action is required to be performed that cannot be performed with the help of standard language operators.
In terms of maintainability, extensibility, readability, in my opinion, the Go language wins, although it loses in wordiness.
Generalized programming in some cases gives us an undeniable benefit. This clearly illustrates the sort package. So, to sort any list, we just need to implement the interface sort.Interface.
import "sort" type Names []string func (ns Names) Len() int { return len(ns) } func (ns Names) Less(i, j int) bool { return ns[i] < ns[j] } func (ns Names) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } func main() { names := Names{"London", "Berlin", "Rim"} sort.Sort(names) }
If you take any open source project and execute the grep "interface {}" -R command, you will see how often the used interfaces are used. Comrade comrades will immediately say that all this is due to the lack of generics. However, this is not always the case. Take for example the language DELPHI. Despite the fact that he has these same generics, it contains a special type of VARIANT for operations with arbitrary data types. The Go language does the same.
And the straitjacket must match the size of the madness. Stanislav Lec.
Many extreme lovers can say that Go has another mechanism for creating generics - reflection. And they will be right ... but only in rare cases.
Rob Pike warns us:
It is a powerful tool that should be used with care. It should be avoided as long as it is not strictly necessary.
Wikipedia tells us the following:
Reflection means a process during which a program can monitor and modify its own structure and behavior at run time. The programming paradigm underlying reflection is called reflexive programming. This is a type of metaprogramming.
However, as you know, you have to pay for everything. In this case, it is:
Therefore, it is necessary to use reflection with caution, as tools of large caliber. Thoughtless use of reflection leads to unreadable programs, constant errors and low speed. Just the fact that a snob-programmer could show off his code in front of other, more pragmatic and modest colleagues.
Along with the state, heirs are also left with debts.
Despite the fact that many people believe that the language is completely based on the legacy of C, this is not the case. The language has incorporated many aspects of the best programming languages.
First of all, the syntax of grammatical structures is based on the syntax of the C language. However, the DELPHI language also had a significant impact. So, we see that the excess brackets are completely removed, so greatly reducing the readability of the program. The language also contains the operator ": =" inherent in the DELPHI language. The concept of packages is borrowed from languages like ADA. The declaration of unused entities is borrowed from the PROLOG language.
The basis of the packages was taken the semantics of the language DELPHI. Each package encapsulates data and code and contains private and public entities. This allows the package interface to be reduced to a minimum.
The implementation of the delegation method was borrowed from the DELPHI language.
No wonder there is a joke: Go was developed while the C program was compiled. One of the strengths of the language is super-fast compilation. The idea was borrowed from the DELPHI language. In addition, each Go package corresponds to the DELPHI module. These packages are recompiled only when really necessary. Therefore, after the next edit, it is not necessary to compile the entire program, and it is enough to recompile only the modified packages and packages that depend on these modified packages (and only then, if the package interfaces have changed).
The language contains many different high-level constructions that are not related to low-level C-type languages.
Memory management generally deserves a separate article. If in languages like C ++, control is completely given to the developer, in later languages like DELPHI, the reference counting model was used. With this approach, cyclic references were not allowed, since lost clusters were formed, then Go is integrated with detection of such clusters (as in C #). In addition, garbage collector surpasses most currently known implementations in performance and can already be used for many real-time tasks. The language itself recognizes situations where the value for storing a variable can be allocated on the stack. This reduces the load on the memory manager and increases the speed of the program.
The parallelism and competitiveness of language is beyond praise. No low-level language can even remotely compete with the Go language. In fairness, it is worth noting that the model was not invented by the authors of the language, but simply borrowed from the good old ADA language. The language is able to handle millions of parallel connections involving all CPUs, while having an order less than the typical for multi-threaded code complex problems with deadlocks and race conditions.
If it is profitable, everyone will become disinterested.
The language also provides us with a number of undoubted benefits:
Complicate is simple, difficult to simplify.
Go was designed to be simple and he succeeded in this goal. It was written for smart programmers who understand all the advantages of teamwork and are tired of the infinite variability of Enterprise level languages. Having a relatively small set of syntactic structures in its arsenal, it is almost not subject to change over time, so developers have plenty of time freed up precisely for development, and not for endless learning of language innovations.
Companies also receive a number of advantages: a low entry threshold allows you to quickly find a specialist, and the immutability of the language allows you to use the same code 10 years later.
The large size of the brain has not made a single elephant the Nobel Prize.
For those programmers whose personal ego prevails over team spirit, as well as theorists who love academic tasks and endless "self-improvement", the language is really bad, because it is a general-purpose artisan language that does not allow one to get aesthetic pleasure from the result of his work and show himself a professional in front of colleagues (provided that we measure the mind with these criteria, and not with the IQ coefficient). Like everything in life - this is a matter of personal priorities. Like all upcoming innovations, the language has already come a long way from general denial to mass recognition. The language is ingenious in its simplicity, and, as you know, everything ingenious is simple!
Source: https://habr.com/ru/post/448932/
All Articles