Gopher - Go Mascot
Some time ago I began to study the possibility of using Go in some of my third-party projects and was simply amazed by the beauty of this programming language.
I think that its developers managed to find a balance between ease of use, usually typical of interpreted languages ​​with dynamic typing, and performance, coupled with security (type safety, memory usage safety), which are characteristic of compiled languages ​​with static typing.
There are two more features of the language that make it an ideal option for developing modern systems. I will discuss them in more detail in the section of the article entitled “Strengths”.
One of them is first-class support for competitiveness (concurrency) (using goroutines and channels, discussed below). By definition, competitiveness makes it possible to more efficiently use all the available CPU power, even if the processor has only one core. In Go, hundreds of thousands of gorutin (lightweight streams) can run simultaneously on a single machine. Channels and gortines are extremely important when building distributed systems, since they abstract the mechanisms associated with message transfer as part of the supplier-consumer messaging paradigm concept.
Back in Go, I really like the interfaces. Interfaces allow you to create a system based on loosely coupled or completely unrelated components. Thus, your code can rely on the interface type, without particularly caring about who implements this interface and how. The controller provides this code with a dependency corresponding to the interface (implementing all its functions). This allows you to create a good architecture for unit testing (using dependency injection). The controller can inject (inject) a simulation of the implementation of the interface required in the code, which will give an opportunity to test the correctness of its operation.
Considering all these features, I think you can say that Go is a wonderful language, especially when it comes to developing cloud (web servers, CDN, caches, etc.) and distributed systems, microservices, etc. Therefore, if you Now thinking about the choice of language for the next project, I recommend to take a closer look at Go.
In this article I plan to talk about the following aspects of the language:
Go is an open source language created by Google by Robert Griesemer, Rob Pike, and Ken Thompson. The openness of the source code in this case means that everyone can take part in the development of the language, making suggestions for developing new functions, correcting errors, etc. The language code is available on GitHub . Documentation on participation in the development can be found here .
The authors say that the main reason for creating a new language was the desire to solve the problems arising in Google related to software development. They also mention that Go was developed as an alternative to C ++.
Here is what Rob Pike says about Go:
“Goal's goal is not to conduct research in the field of programming language design; it is to improve the working environment of its developers and their colleagues. Go is more focused on software development than on programming language research. Or, in other words, Go was designed to serve primarily development goals. ”
Google's problems that pushed to create Go (taken from https://talks.golang.org/2012/splash.article ):
In order for Go to succeed, he must solve these problems (taken from https://talks.golang.org/2012/splash.article ):
Go is a language for creating systems. It is great for cloud solutions (web servers, caches), microservices and distributed systems (at the expense of competitiveness).
Static typing
Go is a static-typed language. This means that already at the compilation stage it is necessary to declare the types of all variables, function arguments and return values. This approach may seem inconvenient, but this is actually an advantage, since many errors will be found at compile time. The importance of this factor grows with the size of the command, since type declaration makes libraries and functions more readable and better understood.
Compilation speed
The code written in Go compiles very quickly , so you no longer need to wait for the compilation to end :) In fact, the go run
command starts the program so quickly that at first it even seems that there was no compilation at all, as if we were working with an interpreted language.
Execution speed
The Go code is compiled directly into machine code, which depends on the operating system used (Linux / Windows / Mac) and the CPU architecture of the machine (x86, x86–64, ARM, etc.) on which the compilation is performed. Therefore, Go-programs work really fast.
Portability
Since the program is compiled directly into machine code, binary files are portable within the same operating system and architecture.
This possibility arises because Go binaries are statically linked, that is, all the necessary shared libraries are included in the binary file at the compilation stage. Dynamically while the program is running, they are not linked.
This is convenient when deploying programs on a large number of machines in the data center. The binary can be simply copied (for example, using scp
) to any number of machines, if, of course, it was compiled for the OS and the architecture of the target system. You do not need to care about the installed version of Linux and manage dependencies. The program just starts and works :)
Competitiveness
Go provides high-quality support for competitiveness, which is one of the most important advantages of this programming language. Go's competitiveness implementation mechanisms are based on Tony Hoare's 'Communicating Sequential Processes' .
Go Runtime allows you to perform hundreds of thousands of competitive gorutin on a single machine. Gorutina is a lightweight flow of execution. Go can multiplex gorutiny in OS threads. That is, several gorutin can competitively run in the same thread of the operating system. In the Go runtime, there is a special scheduler whose task is to control the execution of the gorutin.
There are two advantages to this approach:
When initializing, the gorutin takes up only 4 KB in the stack. This is minuscule compared to, for example, 1 megabyte, which is usually allocated to the operating system stream. This number becomes important when it is necessary to perform competitively hundreds of thousands of different gorutin on one machine. If you use OS threads for these purposes, RAM can run out very quickly.
Since I have repeatedly mentioned “competitiveness” in this article, I recommend watching a video in which Rob Pike says that competitiveness and parallelism are two different things . In programming, competitiveness means a set of independently running processes, while as parallelism is the simultaneous execution of independent and / or dependent calculations. On one core, parallelism is not available, since it can perform only one operation per unit of time. However, competition in this case is still possible. The operating system scheduler runs different processes (actually, threads, since each process has at least the main thread) in different time slices. Thus, in a unit of time, the processor is occupied by only one thread. Due to the high speed of operations, it seems to us that several programs are simultaneously running. But in fact all pass one by one.
Competition allows you to deal with several things at once, and concurrency allows you to do several things at once.
Interfaces
Interfaces allow you to create loosely coupled systems. In Go, an interface type can be defined as a set of functions. And it's all. Any type that implements these functions implicitly implements an interface, that is, you do not need to specify that your type implements a specific interface. This will make the compiler at compile time.
Thus, your code can rely on the interface type, without particularly caring about who implements this interface and how. The controller provides this code with a dependency corresponding to the interface (implementing all its functions). This allows you to create a good architecture for unit testing (using dependency injection). The controller can inject (inject) a simulation of the implementation of the interface required in the code, which will give an opportunity to test the correctness of its operation.
Here there is an advantage from the point of view of microservice architecture. If your application is hosted on one server (at the beginning of the project), you design it in such a way that its functions are performed by different microservices, each of which implements its own interface. Thus, other services / controllers invoke interface methods without worrying about how they are implemented internally.
Garbage collection
Unlike C, Go does not need to free up dynamically allocated memory and worry about dangling pointers. The garbage collector will take care of them.
No exceptions
Handle errors yourself. I am very pleased with the fact that Go, unlike many other languages, does not have a standard mechanism for dealing with exceptions. Developers here have to independently handle errors like 'couldn't open file' - there is no possibility to wrap the code in a try catch
. That is, programmers are encouraged to think about how these errors should be properly processed.
Wonderful toolkit
One of the most enjoyable sides of Go is its tools:
Gofmt automatically formats the code and indents. This can ensure that the Go code looks the same for all developers without exception writing in this language, thus significantly improving the readability of the code.
Go run compiles and executes code. Yes, Go programs need to be compiled, but this happens so quickly that there is a feeling of working with the interpreted language.
Go get loads the library from GitHub and copies it to GoPath, after which it can be imported into the project.
These and other tools can be found here .
Remarkable built-in libraries
Go’s has the following built-in libraries that are designed to make the life of a modern developer easier:
Libraries and frameworks for all kinds of usage scenarios can be found here .
Lack of generics
Generics allow you to create algorithms using types that will be specified later. Suppose you need to write a function to sort the integers. Then you needed a function to sort the list of strings. At this point, you understand that the code of these functions will be almost the same, but you cannot use the first function, because it takes a list of integers as an argument and refuses to receive a list of strings. Nothing can be done, you will have to duplicate the code. If the generics were in Go, you could write a function to sort the list of elements of type T and call the same function for integers, strings, and any other types for which there is an ordering function. That is, the compiler must be able to compare values ​​of this type.
In Go, it is possible to do something similar to a generic using an empty interface (interface). But this decision is not without flaws.
Generics - a very controversial topic. There are programmers who pray for them when others do not want to include generics in the language, since they represent a compromise between compile time and program execution time.
The creators of Go say they are open to dialogue about the implementation of the generics mechanism in Go. But it's not just generics. They can be implemented in the language only if they interact well with its other components. Let's wait for Go 2 and see if there is any solution to this problem.
Lack of dependency management
“Go 1 promise” (Go 1 promise) allows you to be sure that Go itself and its libraries will not change their API during Go 1 life. Your code must be compiled in Go 1.5 and Go 1.9. Most third-party libraries follow this promise. Since you are downloading third-party libraries from GitHub using go get
by executing go get github.com/vendor/library
, you can only hope that there is no change in the API in the newest code in the master branch. For rare third-party projects, this is not so bad, since most libraries hold the word, but for production the option is not the best.
Ideally, you need some kind of versioning of dependencies, in which you could specify the version number of the third-party library in the dependency file. In this case, you will not have to worry about changing the API, as this will not affect the version you are using. Later it will be possible to study the differences and decide on the transition to the new version, make the appropriate changes in your code, if necessary, and indicate the new version number in the dependency file.
Go runs an official experiment called dep , which I hope will solve this problem. Perhaps this will happen in Go 2 :)
I really like the open source approach to creating a language that we see in Go. If you want the functionality you need to be implemented in Go 2, issue the following document:
The authors will review the proposal and render their verdict. Discussions on these issues are held publicly in mailing lists and issue trackers.
In my opinion, the two most serious problems of Go are generics and dependency management. Dependency management is more concerned with working with releases or selecting tools. I hope that dep will be the official solution to this problem. Considering that the authors speak about the openness of their position in relation to generics, I am very curious how they are going to implement them, since generics will inevitably increase either the compile time or the execution time.
I want to quote here the most memorable quotes from Rob Pike’s presentation titled Simplicity is Complicated .
Traditionally, programming languages ​​follow the path of increasing functionality. Thus, they constantly inflate and complicate their compilers and specifications. If this continues in the future, in the future all languages ​​will become almost the same. A good example is adding OOP in javascript. Many things were intentionally not included in Go by its authors. Only that functionality was implemented, with respect to which there was a unity of opinions of the authors of the language, those possibilities that really increased the Go utility.
Elements of functionality resemble orthogonal vectors in a solution space. The ability to choose the appropriate vectors for your case is important. Selected vectors should easily interact with each other, that is, all elements of the language functionality should fit in a predictable way. A complete set of language functionality elements covers the entire solution space. Implementing all sorts of features, given the need for their seamless interaction, brings additional complexity to languages. However, language abstracts complexity by providing interfaces that are simple and easy to understand. Thus, simplicity is the art of hiding complexity :)
Readability also means reliability. If the complexity of the language is high, you need more knowledge and effort to read the code and work with it, including debugging and patching. It also means that beginners will have to spend more time to bring their understanding of the language to a level where they can begin to contribute to the development of the code base.
Taken from https://blog.digitalocean.com/get-your-development-team-started-with-go/
The Go distribution and installation instructions can be found here .
Here is the official getting started guide . I also recommend Go in examples .
If you want a book, The Go Programming Language is a great option. It is written in the same spirit as the legendary C Programming Language by Alan AA Donovan and Brian W. Kernighan .
If you want to chat with community members, you can join the Gophers Slack Channel .
Many companies began to invest in Go. Here are some of the most famous:
Thank you for your time. If you liked this article, I will be very grateful if you share a link to it with your friends on Twitter / Facebook. I can be found on Twitter: https://twitter.com/kanishkdudeja
References:
Source: https://habr.com/ru/post/341302/
All Articles