Go-developer New Year's promises

Original author: Russ Cox
  • Transfer

image
Photo by Roman Pronskiy


This is a translation of the post of one of the main developers of the Go language, Russ Cox, where he gives himself promises in the traditional New Year time format and plans to fulfill them.


The time for making decisions came, and I thought it would make sense to talk a little bit about what I want to work on this year in relation to Go.


Every year I set a goal - to help Go-developers . I want to be sure that what the creators of Go do has a positive effect on all Go developers. Because they have a lot of ways to make a mistake: for example, you can spend too much time cleaning or optimizing code that does not need it; respond only to the most common or recent complaints and inquiries; focusing unnecessarily on short-term improvements. Therefore, it is so important to look at everything from the outside and do what will bring the most benefit to the Go-community.


In this article, I will describe some of the main tasks that I will focus on this year. This is my own list, not the entire Go team.


First of all, I want to get feedback on everything that is written here. Secondly, I want to show that I really consider the problems described below important. I think that people too often perceive the lack of activity on the part of the Go team as a signal that everything is so fine, although in fact we just solve other, more priority tasks.


Type Aliases


We have a constantly pop-up problem with moving types from one package to another with large-scale codebase refactoring. We tried to solve it last year using common aliases , but this did not work: we explained the essence of the changes too poorly, and the changes themselves were late, the code was not ready for Go 1.8. Learning from this experience, I spoke and wrote an article about the underlying problem, and this gave rise to a productive discussion in the Go bug tracker about possible solutions. It seems that introducing more limited type aliases would be the right next step. Hope they appear in Go 1.9. # 18130


Package management


In February 2010, I developed support for Go for downloading published packages (goinstall, which became go get). Since then a lot has happened. In particular, ecosystems of other languages ​​have seriously raised the bar for package management systems, and the OpenSource community is mostly in agreement with semantic versioning , which provides a basic understanding of the compatibility of different versions. Here Go needs to be improved, and a group of people are already working on a solution . I want to make sure that these ideas are correctly integrated into standard Go tools. I want package management to be another advantage of Go.


Build System Improvements


The build team gohas a number of shortcomings that need to be fixed. Below are three typical examples to which I intend to devote my work.


Builds can be very slow because the utility godoes not actively cache the build results. Many do not understand that they are go installsaving the results of their work, but go buildno, that’s why they run it over go buildand over and the assembly is expected to be slow. The same applies to duplicates go testwithout the go test –ipresence of modified dependencies. Whenever possible, all types of assemblies should be incremental. # 4719


Test results also need to be cached: if the input data has not changed, then usually there is no need to restart the test itself. This will greatly reduce the cost of running “all tests” provided that there are minor changes or no changes. # 11193


Work outside GOPATH should be supported in much the same way as work inside it. In particular, it should be possible to start git clone, enter the directory with cd, and then run the command go, and so it all works perfectly. Package management only increases the importance of this task: you should be able to work with different versions of the package (say, v1 and v2) without having to maintain separate GOPATHs for them. # 17271


Basic Code Set


It seems to me that my presentation and article on code base refactoring benefited from concrete examples from real projects. We also came to the conclusion that adding to vet should solve problems specific to real programs. I would like this analysis of real practice to become a standard way of discussing and evaluating changes in Go.


Now there is no generally accepted characteristic set of code to conduct such an analysis: everyone must first create their own project, and this is too much work. I would like to put together a single, standalone Git repository containing our official core set of code for analysis that the community can check against. A possible starting point is the top 100 Go repositories on GitHub by the number of stars, forks, or both.


Automatic vet


The Go distribution comes with a powerful tool - go vetwhich indicates common errors. The bar for these errors is high, so you need to listen to his messages. But the main thing is to remember to run vet. Although it would be more convenient not to remember this. I think that we could run vet in parallel with the final compilation and linking of the binary that occur during execution go testwithout any slowdown. If we succeed and if we limit the allowed vet checks to a sample that is 100% accurate, then we can generally turn the vet pass into a precondition for running the test. Then the developers will not need to remember what needs to be launched go vet. They will run go test, and vet will occasionally report something important, avoiding unnecessary debugging. # 18084# 18085


Errors and generally accepted techniques


Part of the common practice for error messages in Go is that functions include a relevant accessible context, including information about which operation was attempted (function name and its arguments). For example, this program


err := os.Remove("/tmp/nonexist")
fmt.Println(err)

displays


remove /tmp/nonexist: no such file or directory

Not all Go code does the same thing as it does os.Remove. A lot of code does just


if err != nil {
    return err
}

all over the call stack and throws out a useful context that should be shown (for example, as remove /tmp/nonexist:above). I would like to understand if we are not mistaken in our expectations for the inclusion of the context, and whether we can do something to make it easier to write code that returns more informative errors.


There are also various discussions in the community about interfaces for clearing errors from the context. And I want to understand when this is justified, and whether we should come up with some kind of official recommendation.


Context and best practices


In Go 1.7, we added a new context package for storing information that is somehow related to the request (for example, about timeouts, whether the request was canceled and about authorization data ). The individual context is immutable (like strings or integer values): you can only get a new, updated context and pass it explicitly down the call stack, or (less commonly) back to the top. Context is passed through API today (e.g. database / sql and net / http) mainly so that they can stop processing the request when the caller no longer needs the result of processing. Information about timeouts is quite suitable for transferring in context, but it is absolutely not suitable for database options, for example, because they are unlikely to be equally well applied to all possible database operations during query execution. What about a time source or a logger? Is it possible to store them in context? I will try to understand and describe the criteria for what can be used in context and what cannot.


Memory model


Unlike other languages, the Go memory model is intentionally modest, not giving many promises to users. In fact, the document says that it’s not worth reading especially. At the same time, it requires the compiler more than in other languages: in particular, the race on integer values ​​is not an excuse for the arbitrary behavior of your program. There are also complete spaces: for example, the sync / atomic package is not mentioned . I think that the developers of the main compiler and runtime-systems will agree with me that these atoms should behave in the same way as seqcst-atoms in C ++ or volatile in Java. But at the same time, we must neatly enter this into the memory model and into the long, long blog post. # 5045 # 7948 # 9442


Immutability


The Race detector is one of Go's favorite features. But not having a race at all would be even better. I would really like for there to be a reasonable way to integrate link immutability into Go , so that programmers can make clear, verified judgments about what can and cannot be written, thereby preventing certain race conditions from being compiled. Go already has one immutable type— string; it would be nice to retroactively determine what stringis a named type (or alias) for an immutable one []byte. I don’t think it will be possible to implement this year, but I want to figure out possible solutions. Javari, Midori, Pony and Rust have already identified interesting options, in addition there is a whole series of studies on this topic.


In the long run, if we can statically eliminate the possibility of a race condition, this will allow us to abandon most of the memory model. Perhaps this is a pipe dream, but again, I would like to better understand potential solutions.


Generics


The hottest debate between Go developers and programmers in other languages ​​has erupted over whether Go should support generics (or how long this should have happened). As far as I know, the Go team has never said Go doesn't need generics. We said that there are more important tasks that need to be addressed. For example, I believe that improving package management support will have a much stronger positive effect on most Go developers than introducing generics. But at the same time, we are aware that in some cases the lack of parametric polymorphism is a serious obstacle.


Personally, I would like to be able to write general functions for working with channels, for example:


// Join делает все полученные во входных каналах сообщения 
// доступными для получения из возвращаемого канала.
func Join(inputs ...<-chan T) <-chan T
// Dup дублирует сообщени из c и в c1 и в c2
func Dup(c <-chan T) (c1, c2 <-chan T)

I would also like to be able to write on Go higher-level data processing abstractions, similar to FlumeJava or LINQ , so that type mismatch errors are caught at compilation rather than at runtime. You can write a bunch of data structures or algorithms using generics, but I think that such high-level tools are more interesting.


На протяжении нескольких лет мы старались найти правильный способ внедрения дженериков в Go. Последние несколько предложений касались разработки решения, которое обеспечивало бы общий параметрический полиморфизм (как chan T) и унификацию string и []byte. Если последнее решается параметризацией на основе неизменяемости, как описано в предыдущей части, тогда, возможно, это упростит требования к архитектуре дженериков.


Когда в 2008 я впервые задумался о дженериках в Go, главными примерами были C#, Java, Haskell и ML. Но ни один из подходов, реализованных в этих языках, не выглядел идеально подходящим для Go. Сегодня есть более свежие решения, например, в Dart, Midori, Rust и Swift.


Several years have passed since we dared to tackle this topic and consider possible options. Probably, it's time to look around again, especially in the light of information on mutability / immutability and solutions from new languages. I don’t think that generics will appear in Go this year, but I would like to understand this issue.


Also popular now: