From c55ef48ba30ea6277bc5d776700472298479db42 Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Fri, 6 May 2022 20:50:48 +0000 Subject: [PATCH] blog/we-have-go-2: write generics section Signed-off-by: Xe Iaso --- blog/we-have-go-2.markdown | 117 +++++++++++++++++++++++++++++++------ 1 file changed, 99 insertions(+), 18 deletions(-) diff --git a/blog/we-have-go-2.markdown b/blog/we-have-go-2.markdown index d4407b6..2a264e8 100644 --- a/blog/we-have-go-2.markdown +++ b/blog/we-have-go-2.markdown @@ -89,6 +89,21 @@ nonzero amount of work. The bootstrapping can be made simpler with compatible with the semantics and user experience of the Go compiler that Google makes. +Another key thing porting the compiler to Go unlocks is the ability to compile +Go packages in parallel. Back when the compiler was written in C, the main point +of parallelism was the fact that each Go package was compiled in parallel. This +lead to people splitting up bigger packages into smaller sub-packages in order +to speedhack the compiler. Having the compiler be written in Go means that the +compiler can take advantage of Go features like its dead-simple concurrency +primitives to spread the load out across all the cores on the machine. + +The Go compiler is fast sure, but +over a certain point having each package be compiled in a single-threaded manner +adds up and can make build times slow. This was a lot worse when things like the +AWS, GCP and Kubernetes client libraries had everything in one big package. +Building those packages could take minutes, which is very long in Go +time. + ## Go Modules In Go's dependency model, you have a folder that contains all your Go code @@ -719,14 +734,12 @@ assert that values have behaviors and then you're off to the races. I end up missing the brutal simplicity of Go interfaces in other languages like Rust. -### Introducing Go Generics +### Introducing Go Generics In Go 1.18, support for adding types as parameters to other types was added. This allows you to define constraints on what types are accepted by a function, so that you can reuse the same logic for multiple different kinds of underlying -types or write collections that deal with values of a given type that meets an -interface without also having to make sure that everything else in that -collection is of the same type at runtime. +types. That `doSomething` function from above could be rewritten like this with generics: @@ -739,21 +752,89 @@ func doSomething[T Quacker](qs []T) { } ``` -We can totally refactor out the error return and any of that runtime fallible -code. This allows us to express constraints at _compile time_ so that -attempting to mix `Duck`s and `Sheep` in the same argument to `doSomething` -will fail to build. +However this doesn't currently let you avoid mixing types of `Quacker`s at +compile time like I assumed while I was writing the first version of this +article. This does however let you write code like this: -- [ ] Overview of some of the types of collections it lets you make -- [ ] This is a huge improvement to the language +```go +doSomething([]Duck{{}, {}, {}}) +doSomething([]Sheep{{}, {}, {}}) +``` + +And then this will reject anything that _is not a `Quacker`_ at compile time: + +```go +doSomething([]string{"hi there this won't work"}) +``` + +``` +./prog.go:20:13: string does not implement Quacker (missing Quack method) +``` + +### Unions + +This also lets you create untagged union types, or types that can be a range of +other types. These are typically useful when writing parsers or other similar +things. + +It's frankly kind of fascinating that +something made by Google would even let you _think_ about the word "union" when +using it. + +Here's an example of a union type of several different kinds of values that you +could realistically see in a parser for a language like [LOLCODE](http://www.lolcode.org/): + +```go +// Value can hold any LOLCODE value as defined by the LOLCODE 1.2 spec[1]. +// +// [1]: https://github.com/justinmeza/lolcode-spec/blob/master/v1.2/lolcode-spec-v1.2.md#types +type Value interface { + int64 // NUMBR + float64 // NUMBAR + string // YARN + bool // TROOF + struct{} // NOOB +} +``` + +This is similar to making something like an +[`enum`](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html) in Rust, +except that there isn't any tag for what the data could be. You still have to do +a type-assertion over every value it _could_ be, but you can do it with only the +subset of values listed in the interface vs any possible type ever made. This +makes it easier to constrain what values can be so you can focus more on your +parsing code and less on defensively programming around variable types. + +This adds up to a huge improvement to the language, making things that were +previously very tedious and difficult very easy. You can make your own +generic collections (such as a B-Tree) and take advantages of packages like +[`golang.org/x/exp/slices`](https://pkg.go.dev/golang.org/x/exp/slices) to avoid +the repetition of having to define utility functions for every single type you +use in a program. + +I'm barely scratching the surface with +generics here, please see the [type parameters proposal +document](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md) +for a lot more information on how generics work. This is a well-written thing +and I highly suggest reading this at least once before you try to use generics +in your Go code. I've been watching this all develop from afar and I'm very +happy with what we have so far (the only things I'd want would be a bit more +ability to be precise about what you are allowing with slices and maps as +function arguments). --- -We already have Go 2. It’s just called Go 1.18 for some reason. It’s got so many -improvements and fundamental changes that I believe that this is already Go 2 in -spirit. I, as some random person on the internet that is not associated with the -Go team, think that if there was sufficient political will that they could -probably label what we have as Go 2, but I don’t think that is going to happen -any time soon. Until then, we still have a very great set of building blocks -that allow you to make easy to maintain production quality services, and I don’t -see that changing any time soon. +In conclusion, I believe that we already have Go 2. It’s just called Go 1.18 for +some reason. It’s got so many improvements and fundamental changes that I +believe that this is already Go 2 in spirit. There are so many other things that +I'm not covering here (mostly because this post is so long already) like +fuzzing, RISC-V support, binary/octal/hexadecimal/imaginary number literals, +WebAssembly support, so many garbage collector improvements and more. This has +added up to make Go a fantastic choice for developing server-side applications. + +I, as some random person on the +internet that is not associated with the Go team, think that if there was +sufficient political will that they could probably label what we have as Go 2, +but I don’t think that is going to happen any time soon. Until then, we still +have a very great set of building blocks that allow you to make easy to maintain +production quality services, and I don’t see that changing any time soon.