
Generics in Go with Ian Lance Taylor (2019) - pjmlp
https://changelog.com/gotime/98
======
bpodgursky
I just don't want to hear about Golang generics any more, until they are in a
usable beta. It's been years, and while I'm confident that there are some
maintainers who really for real want to add it... it's hard to shake the
feeling that the talk is going to go around in circles, hoping devs give up
and forget about it.

It's like sending a project into an environmental review. It sounds
constructive, until you realize it's a nice way of black-holeing the project.

I really apologize if I'm being unfair to the (team?) working on this, but...
it's been years. Generics have been around for decades. It's important to get
it right, but Golang is by design _not that complicated_. There really doesn't
need to be wild new R&D to figure out how to pack generics into a programming
language.

</salt> rant over. For now.

Edit: And seriously, it's 100% fine if the answer is "we thought about it, and
decided not to do it." It's just frustrating being strung along with constant
design proposals, iterating on syntax, and then... back to more design
proposals. Just make a choice (or a veto), and go with it.

~~~
hinkley
I suspect the problem is that Generics bring you face to face with the Liskov
Substitution Principle. For most of us it's just a good idea, and then you try
to do generics and all of a sudden it begins to become the law.

So you try to layer it onto a language like Java and you discover that an
ArrayList of Integer is not interchangeable with an ArrayList of Number
because you can stick a Double in one of them and blow up everyone expecting
only whole numbers.

I've long suspected that you either have to put Generics in at the beginning
or prepare for decades of complaints about your workarounds. And getting stuck
in committee for a very, very, very long time.

~~~
lazulicurio
> So [...] you discover that an ArrayList of Integer is not interchangeable
> with an ArrayList of Number because you can stick a Double in one of them
> and blow up everyone expecting only whole numbers.

Is this really an issue with Java's type system though? If you get an
ArrayList<Number> and you assume that it contains only whole numbers, that's
on you.

FWIW, my personal opinion is that erasure is better than templated generics
for most use cases. Even when it comes to performance: a good JIT should be
able to specialize generic code that end up on a hot path. Erasure does cause
some issues with reflection and value types, but I think that it provides a
better foundation to start from when designing a type system.

Then again, I am quite a fan of Rust's generics and dyn/impl {Trait}. Might be
worth doing something similar where you have two separate generic systems: one
for boxed values, and one for unboxed values.

~~~
esrauch
> Is this really an issue with Java's type system though? If you get an
> ArrayList<Number> and you assume that it contains only whole numbers, that's
> on you.

The point is that you have an List<Integer> are you allowed to call a function
that takes an List<Number>? The answer is effectively "yes it should be
allowed" if its not mutating the List (all Integers can be safely read as
Numbers) but no if the List is mutable (you can't handle the fact that it
might insert a non-Integer number into the Integer-only List).

~~~
joshuamorton
This is less LSP and more the co/contravariance of container types, which is
its own special hell.

~~~
LessDmesg
The hell stems from inheritance, which is just syntactic sugar over
composition. And a very unhealthy kind of sugar as that: problems with co- and
contravariance, problems with copying, the need for redundant type
declarations. And inheritance doesn't guarantee autofulfillment of the Liskov
principle, either. One might even wonder why inheritance is needed at all in a
language. Go and Rust are certainly fine without it.

~~~
hinkley
One of the humblest smart guys I worked with years ago would try to convince
me over lunch that the Next Big Language would have syntactic sugar to make
composition as easy as inheritance. I think in part because I had intimated
that I was trying to create a language (narrator: he didn't. It went nowhere)

There are a couple languages that have added something like this in the
interim, but nobody seems to have made it an idiomatic thing. Which is a
shame, because I still think I might like to work in the world he dreamed up.

You get a little more composition in languages like NodeJS for the very
pragmatic reason that inheritance, at least historically, was not seamless and
hence had a high degree of friction.

~~~
pjmlp
Ah, just like VB did with COM....

------
kristianp
Ian's talk about generics at gophercon 2019:
[https://www.youtube.com/watch?v=WzgLqE-3IhY](https://www.youtube.com/watch?v=WzgLqE-3IhY)

The proposal seems so simple, I want to use it already! It would be a nice
idea to have a translator from generic go to stable go, so that users of go
can start using these generics.

Edit: an implementation was linked in the talk as a changelist to try it out:
[https://go-review.googlesource.com/c/go/+/187317/](https://go-
review.googlesource.com/c/go/+/187317/)

~~~
kristianp
Anyone know why they didn't go with angle brackets? People familiar with C++
and Java would find it familiar. I wonder if its too unpythonic, or makes the
parsing too hard.

e.g. Instead of

    
    
        func Reverse (type  Element) (s []Element) {
    

why not do:

    
    
        func Reverse<Element> (s []Element) {

~~~
pjmlp
Ian mentions it would make the parser contex aware, which isn't needed
currently.

~~~
norswap
That at least isn't true in terms of the Chomsky grammar hierarchy. You can
have both angle brackets operators and delimiters in a CFG (context-free
grammar). It might be a limitation of their parsing tech (e.g. LL(1) would
have that issue, probably LR too).

No problem for general CFG parsers though (if you can write a BNF grammar for
it, that's the CFG grammar for it).

~~~
pjmlp
Most likely, just repeating his answer on the podcast.

------
haolez
Maybe it's a bit off topic, but I wonder how much the Kubernetes code base
influences this kind of demand to add features to the language.

~~~
pansa2
Why Kubernetes in particular?

~~~
gen220
Because it’s huge, very abstract (pluggable components are a first class
concept), very publicly-known (it’s name carries weight), and has numerous
numerous active contributors, whose membership has evolved continuously over
time. It makes use of a lot of magic to get over a lack of genetics, and has
innovated in that case.

There aren’t a lot of other projects with that profile in Go. etcd and raft
are similar, but are maybe an order of magnitude less “big”.

------
vvern
One concern I have about the shape of the generics proposal as it stands is
that it makes it hard to use arrays of types inside of generic data
structures. Block-based data structures are a staple of efficient programming.
Having a generic B-Tree-backed sorted map would be wonderful. Also wonderful
would be a library implementing a dequeue backed by a linked list of ring-
buffers.

Maybe I'm missing something in the proposal but it seems like it's awkward to
build these in the current proposal with contracts as type lists and methods.
One option is to implement a `slice()` method on your array types and then
interact with them as such.

```

interface Array(A, T) { A slice() []T }

type Dequeue(type A, T Array) struct { ... }

```

Then you'd do something like:

```

type intArray8 [8]int

func (ia intArray8) slice() []int { return ia[:] }

func newIntDequeue() *Dequeue(intArray, int) { ... }

```

But there's something that feels dirty about that.

------
nif2ee
Golang is a pretty much messed up language made by short-sighted and very,
very, very stubborn designers. If you don't believe me, just look for instance
at all the hacky code generation tools made by the Kubernetes guys. But even
if generics are there, the language itself is still very lacking to an
irritating level (e.g. no option types aka nil access, no enforcement of error
checking aka no result types, no enums, no conditional compilation, no
iterators, no immutables, etc...)

EDIT: HN upvote system has nothing to do with right or wrong or constructing a
good discussion, it's based on fanboyism and political correctness. Really
sad.

~~~
tschellenbach
Go has been amazing here at Stream. No way we would be able to power chat and
feeds for 500 million end users with such as small team without Go. C++ would
be a valid option in terms of raw performance, but the development overhead is
just too large. Go hits a beautiful sweet spot between performance and
developer productivity.

~~~
CameronNemo
I am not sure how you refute anything the above poster said. They did not
mention performance once ( _cough_ Rust _cough_ ) and that seems to be most of
your point. You did not even say _why_ go is so good for developer
productivity... the poster made many points that could be used to argue that
go does not promote developer productivity.

~~~
sacado2
> the poster made many points that could be used to argue that go does not
> promote developer productivity.

I think go promotes maintainability rather than developer productivity. IMO,
go authors favored ease of code reading to the expense of code writers. That
being said...

> no option types aka nil access, no enforcement of error checking aka no
> result types, no enums, no conditional compilation, no iterators, no
> immutables, etc...

No enforcement of error is not true, you have to explicitely ignore an error
if you want to. You have almost the same problem with rust, where you can
unwrap things that can fail, thus explicitely ignoring potential errors
(granted, the program will then panic).

No conditional compilation is not true either, you can, eg, put at the top of
your file build tags:

    
    
       // +build !linux
    

This file won't be compiled on linux (you supposedly have another version of
that file for linux systems).

No immutables: can be a problem, const is very limited indeed in go.

Nil access, yes, although contrarily to C/C++, you can't dereference nil
without having the program panic. AFAICT I never had them happen in
production, when I have a panic it's because of an off-by-one error in a
slice, usually (but then I'd have the same problem with `safe` languages like
rust).

I'd be glad to have enums. Go's workaround is safer than C's enums, but not by
much.

~~~
apta
> I think go promotes maintainability rather than developer productivity.

It seems to promote verbosity just for the sake of so called "simplicity".
It's simple (almost dumb) at the language level, which just pushes complexity
elsewhere.

This is just a statement that I find some people repeat without any
substantiation whatsoever.

> you have to explicitely ignore an error if you want to
    
    
        err := foo()
        ...
        err = bar()
        if err != nil { panic(err) }
    

The first error was unintentionally discarded. I've seen this happen in actual
code bases.

~~~
sacado2
> It seems to promote verbosity just for the sake of so called "simplicity".

I don't think simplicity is the goal, I think simplicity is a mean to an end,
and that end is readability. I remember reading some C++ code I didn't write,
and I couldn't understand where the bug was coming from. Turns out the
developer had redefined the () operator (or something like that) so that it
behaved quite the same as expected, except in a few corner cases.

That's the kind of bug hunt go preserves you from, but verbosity is the price
you have to pay.

> The first error was unintentionally discarded. I've seen this happen in
> actual code bases.

You're right, and AFAIK linters don't catch these.

------
erik_seaberg
> I would say that if the compiler got 100% slower, that would be a failure.

Still a weird thing to care about. If the compiler were 100x slower, that
would still be faster than continually handling boilerplate with _human
brains._

~~~
geodel
Well for that purpose we already have Rust/ Swift and so many other languages.
What is Go's USP apart from fast compiler.

~~~
jitl
Swift and Rust both are known for long build times.

~~~
pjmlp
Mostly caused by LLVM.

Other equally expressive languages have multiple implementations, including
REPLs, with Go like compilation speeds.

So Swift and Rust just need equally LLVM fat free implementations for the
developer loop, leaving the LLVM backend for the blazing execution release
builds.

~~~
camgunz
LLVM isn't fast, but there are other slow parts of the process unique to Rust
[1].

[1]:
[http://gistpreview.github.io/?74d799739504232991c49607d5ce74...](http://gistpreview.github.io/?74d799739504232991c49607d5ce748a)

