blog/we-have-go-2: write generics section

Signed-off-by: Xe Iaso <me@christine.website>
This commit is contained in:
Cadey Ratio 2022-05-06 20:50:48 +00:00
parent bdc85b1536
commit c55ef48ba3
1 changed files with 99 additions and 18 deletions

View File

@ -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.
<xeblog-conv name="Mara" mood="hacker">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.</xeblog-conv>
## 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.
</xeblog-conv>
### 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.
<xeblog-conv name="Numa" mood="delet">It's frankly kind of fascinating that
something made by Google would even let you _think_ about the word "union" when
using it.</xeblog-conv>
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.
<xeblog-conv name="Cadey" mood="enby">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).</xeblog-conv>
---
We already have Go 2. Its just called Go 1.18 for some reason. Its 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 dont 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 dont
see that changing any time soon.
In conclusion, I believe that we already have Go 2. Its just called Go 1.18 for
some reason. Its 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 dont 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 dont see that changing any time soon.