
The Next Step for Generics - ainar-g
https://blog.golang.org/generics-next-step
======
peter_l_downs
Looks like a big step in the right direction. The biggest pain is that methods
cannot contain additional types, which prevents the definition of generic
containers with a map method like

    
    
        func (c Container(T)) Map(transform(type V)(T) V) Container(V)
    

if you want to see what `Result(T)` and `Option(T)` look like under this
proposal, check out a very stupid implementation here
[https://go2goplay.golang.org/p/dYth-
AQ0Fru](https://go2goplay.golang.org/p/dYth-AQ0Fru) It's definitely more
annoying.

But, as per
[https://go.googlesource.com/proposal/+/refs/heads/master/des...](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-
type-parameters.md#methods-may-not-take-additional-type-arguments) it looks
like maybe they will allow additional type parameters in methods, which would
be REALLY nice.

~~~
rogpeppe1
There's no need to use the pointer in there; you could just use an ok bool
instead (saving the indirection):
[https://go2goplay.golang.org/p/4hr8zINfRym](https://go2goplay.golang.org/p/4hr8zINfRym)

I think it's interesting to observe that using the Result type is really not
that much different from using a multiple return value - it's actually worse
in some ways because you end up using "Unwrap" which can panic.

~~~
peter_l_downs
I like your solution, although it's a tradeoff when the value type is some
kind of nested struct whose zero value could be expensive to create.

Agreed that multiple return is actually quite nice in many situations,
although `Unwrap()` is generally only called after checking `OK()` and the
real benefit is in using `Map()`.

    
    
        a, err := DoCalculation()
        if err != nil {
          return err;
        }
        b, err := Transform(a)
        if err != nil {
          return err;
        }
    

is a lot more verbose and harder to read than

    
    
        a := DoCalculation()
        b := ResultMap(a, Transform)

~~~
codezero
I happened upon this comment a bit late, and caveat I'm not really a software
engineer, but this comment made me think of something...

I've written a decent amount of code professionally in the first and second
styles.

I sincerely prefer the second style, for reasons that are hard to articulate,
maybe the aesthetic, the space, or something that I assume is equally as
questionable.

After I stopped writing a lot of code, when I got pulled back in to debug or
provide context long after the fact, in both cases, it was way easier in
"real" code bases, to catch myself back up on things in the case that the code
was of style 1, than when it was of style 2!

I may be alone in this sentiment, and I even regret that I have it.

I think there is also a bit of satisfaction in writing the code in the second
example, especially when the example is a lot more complex than the one
provided here.

Maybe it comes down to how much re-reading/maintenance your codebase actually
needs. I wonder if coding convention/style has been mapped to the types of
problems that code creates and the subsequent repair of those problems being
dependent on the code style... I'm sure if I google it I'll find something :)

------
rb808
I like generics for collections but that is about it. I've seen "gifted"
programmers turn everything into generic classes and functions which then
makes it very difficult for anyone else to figure out what is going on.

One reason I like go is it doesn't have all the bells and whistles that give
too many choices.

~~~
gokhan
What's wrong with this?

    
    
      class Result<T>
      {
        bool IsSuccess {get; set;}
        string Message {get; set;}
        T Data {get; set;}
      }
    

On many occasions, I like using result types for defining a standard response
for calls. It's typed and success / fail can be handled as a cross-cutting
concern.

~~~
AaronFriel
That's a generic container of 0 or 1 elements ;)

It's also incredibly unsafe and why generics aren't enough. C++, Java, and so
on have had generics for ages and with types like the one above, null pointer
exceptions are incredibly common. Nothing prevents the user from attempting to
retrieve the data without first retrieving the success status.

On the other hand, this improves on it dramatically:

    
    
        enum Result<T, E> {
          Success(T),
          Failure(E)
        }

~~~
jcelerier
> It's also incredibly unsafe and why generics aren't enough. C++, Java, and
> so on have had generics for ages and with types like the one above, null
> pointer exceptions are incredibly common.

uh, you'd never get a null-pointer exception in C++ given the type that OP
mentioned. Value types in C++ cannot be null (and most things are value types
by a large margin).

~~~
AaronFriel
Then you can't construct it unless it's successful, no?

A Result<T> that can only contain successful values doesn't seem very useful

~~~
joshuamorton
No, you just are forced to use methods like foo.UnwrapOr(default_value) to get
the Result. Or depending on the language, you get a compile error if you don't
handle both possible values of the Result enum in a switch statement or
if/else clause.

See for example [https://doc.rust-
lang.org/std/result/enum.Result.html#method...](https://doc.rust-
lang.org/std/result/enum.Result.html#method.unwrap_or_default) in rust,
[https://docs.oracle.com/javase/8/docs/api/java/util/Optional...](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#orElse-T-)
in Java, and
[https://en.cppreference.com/w/cpp/utility/optional/value_or](https://en.cppreference.com/w/cpp/utility/optional/value_or)
in C++.

~~~
AaronFriel
Who are you replying to? Is any of your elaboration related to this result
type?

    
    
        class Result<T>
        {
          bool IsSuccess {get; set;}
          string Message {get; set;}
          T Data {get; set;}
        }

~~~
joshuamorton
Ah you're quite correct.

------
cies
I cant help feeling it is a missed opportunity to add generics to Go this
late. A mistake that is copied from earlier languages (C++, Java), a mistake
similar to other mistakes Go chose not to solve at it's inception, like:
having implicit nulls (C, C++, Java, JS, C#), lack of proper sum types (C,
C++, Java, JS) and only one blessed concurrency model (JS).

While I think I get the reasons for these decision in theory, make a simple-
to-fully-understand language that compiles blazingly fast, I still feel it's a
pity (most) these issues where not addressed.

~~~
apta
> having implicit nulls (C, C++, Java, JS, C#),

> lack of proper sum types (C, C++, Java, JS)

Incidentally, Java and C# have addressed (or are in the process of addressing)
both issues. Both languages/platforms are superior to golang in almost every
conceivable way.

~~~
karmakaze
I'm not aware of any current Java project on Sum types. There is multi-catch
for exceptions but no immediate plans I know to use elsewhere.

~~~
richardwhiuk
Java aren't fixing nulls, IMHO. They are making it worse.

~~~
apta
How are they making it worse?

In any case, there already exist solutions in place:

[https://checkerframework.org/manual/#example-
use](https://checkerframework.org/manual/#example-use)

[https://github.com/uber/NullAway](https://github.com/uber/NullAway)

~~~
richardwhiuk
The code ends up looking like:

    
    
      Optional<Integer> foo;
    
      //....
    
      if (foo != null) {
          foo.and_then(new Consumer () {
                function accept(Integer foo) {
    
                }
          });
      }

~~~
karmakaze
I would never assign null to an Optional nor use a library that returned one.

To ensure assignment before use make it final.

------
Animats
This looks like a good, minimal implementation of generics. It's mostly
parameterized types. Go already had parameterized types, but only the built-
ins - maps and channels. Like this.

    
    
        var v chan int
    

Somewhat surprisingly, the new generic types use a different syntax:

    
    
        var v Vector(int)
    

Do you now write

    
    
        v := Vector(int)
    

or

    
    
        v := make(Vector(int))
    

Not sure. The built-in generics now seem more special than they need to be.
This is how languages acquire cruft.

The solution to parameterized types which reference built-in operators is
similarly minimal. You can't define the built in operators for new types.
That's probably a good thing. Operator overloading for non-mathematical
purposes seldom ends well.

They stopped before the template system became Turing-complete (I think) which
prevents people from getting carried away and trying to write programs that
run at compile time.

Overall, code written with this should be readable. C++ templates have reached
the "you are not supposed to understand this" point. Don't need to go there
again.

~~~
throwaway894345
Presumably you write

    
    
        v := Vector(int){}
    

Or

    
    
        v := make(Vector(int))
    

Just like with unparameterized types.

------
kevlar1818
Maybe it's because I'm a newcomer to Go, but I'm surprised by all of the shade
being thrown in the comments here. I think the design doc presents a clean and
simple way to implement generics in Go -- and therefore it's very much in
keeping with the spirit Go. I especially like the constraints system.
Constraining generic types via interfaces just feels "right" for the language.
It leans on a familiar mechanism with just enough leverage.

I'm not without concerns, but I'm struck by conflicting thoughts: I share the
worry that implementing generics will negatively affect Go program readability
which Go developers (rightfully) covet; when you have a hammer, everything
looks like a nail. And yet, I also worry that the Go community is at risk of
analysis paralysis on iterating the language in meaningful ways; the novel and
innovative spirit that created Go must be harnessed to propel it into the
future, lest it dies on the vine.

------
adamch
Finally. Better late than never. I have to write a lot of Go code at $BIGCORP
and keep reaching for generic helper functions like Keys or Map or Filter or
Contains. This is going to make my code much more readable.

~~~
nemo1618
Will it really?

Old:

    
    
      sum := 0
      for _, x := range xs {
          sum += xs
      }
    

New:

    
    
      slices.Reduce(xs, 0, func(i, j int) int {
          return i + j
      })

~~~
hombre_fatal
That's the most compelling example you could come up with as someone who
presumably writes Go?

Reversing a slice of T. Copy and pasting this:

    
    
        for i := len(a)/2 - 1; i >= 0; i-- {
            opp := len(a) - 1 - i
            a[i], a[opp] = a[opp], a[i]
        }
    

New:

    
    
        reverse(a)
    

Or generic versions of these: [https://gobyexample.com/collection-
functions](https://gobyexample.com/collection-functions)

~~~
adamch
Yep, this is exactly the use case I was talking about.

------
conroy
Here's a four-line replacement for the various `sql.Null*` types in the
standard library.

    
    
        type Null(type T) struct {
            Val   T
            Valid bool // Valid is true if Val is not NULL
        }
    

Here's what it looks like in practice:
[https://go2goplay.golang.org/p/Qj8MqYWWAc3](https://go2goplay.golang.org/p/Qj8MqYWWAc3)

~~~
tandr
Nice. But I need to retrain myself to read Go code again, because

    
    
      func Make(type T)(v T) Null(T) {
      }
    

looks really confusing and unreadable to me - too many brackets...

~~~
IshKebab
I agree. I assume they avoided the standard <> brackets to keep things simple,
but really it just makes it harder to parse visually.

~~~
_ph_
I consider the parens () syntax much more readable than the brackets < >

~~~
tandr
Well, you can try to craft a generic function that returns a function that
takes T and returns something of type T, and see how many () you gotta get...

------
binwiederhier
I am glad contracts are being dropped. It was very confusing indeed. Thanks
for listening to the community.

~~~
tapirl
Just the concept name is dropped, the concept itself is still there.

------
atombender
Unlike other commenters, I don't think it's too late to add generics to Go.
I'm looking forward to this.

My only negative reaction is that throughout these proposals, I've not been a
fan of the additional parentheses needed for this syntax. I can think of
several syntaxes that would be more readable, potentially at the expense of
verbosity.

~~~
hu3
You just can't please everyone.

I think that there are benefits when taking the time to gather feedback from
people who actually use the language and make educated decisions with regards
to language features instead of shoving them down in v1.

------
ainar-g
Link to the current draft:

[https://go.googlesource.com/proposal/+/refs/heads/master/des...](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-
type-parameters.md)

~~~
Someone
_“A type parameter with the comparable constraint accepts as a type argument
any comparable type. It permits the use of == and != with values of that type
parameter”_

I think that’s an unfortunate choice. Quite a few other languages use the term
_equatable_ for that, and have _comparable_ for types that have =, ≤ and
similar defined. Using _comparable_ here closes the door for adding that
later.

I also find it unfortunate that they chose

    
    
      type SignedInteger interface {
        type int, int8, int16, int32, int64
      }
    

as that means other libraries cannot extend the set of types satisfying the
constraint.

One couldn’t, for example, have one’s biginteger class reuse a generic _gcd_
or _lcm_ function.

~~~
coder543
Languages have been known to use “Ordered”, “Orderable”, or “Ord” for what
you’re calling “comparable”. Rust and Haskell are both languages that fit this
criteria.

The design draft refers to “constraints.Ordered”, so they’re definitely
thinking about having both “comparable” and “constraints.Ordered”. Although,
for consistency with “comparable”, I think it should be called
“constraints.Orderable”.

~~~
Someone
That’s true, but both rust ([https://doc.rust-
lang.org/beta/std/cmp/trait.Eq.html](https://doc.rust-
lang.org/beta/std/cmp/trait.Eq.html)) and Haskell
([https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-...](https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-
Eq.html#t:Eq)) use _eq_ or similar for what this proposal calls _comparable_ ,
as do C# ( _IEquatable_ ), java (through _Object.equals_ ), and Swift (
_Equatable_ )

~~~
coder543
Sure, but as someone else already pointed out, “comparable” is an established
Go term that refers to “==“ and “!=“ only, and “ordered” refers to the other
comparison operators.

My point was that “comparable” is _not_ universally used in place of the
“Ordered” term that the Go team is using, as you were seemingly implying.
Ordered is a perfectly fine term for it.

You said:

> Using comparable here closes the door for adding that later.

But the door is not closed in any way. It’s just called “constraints.Ordered”,
which is perfectly reasonable.

------
daenz
In my mind, this generics flip-flop will do no good for the long term survival
of Golang. One way to view it is as "listening to the community and changing"
but I think a change this big signals a fracture more than an evolution.

Think about how much planning and foresight goes into making a programming
language, especially one that comes out of a massive top-tier tech company
like Google. They deliberately chose not to include generics and (from what I
remember when I wrote Go code) spent time + effort explaining away the need
for them. Now the decision is being reversed, and it signals to me that they
don't know what they're doing with the language, long term. As a developer,
why do I want to hitch my carts to a language whose core visions are very
clearly not correct or what people want?

~~~
etaioinshrdlu
Or alternatively, they took forever to do it not because they don't like
generics, but because they are super conservative with design and wanted to do
it right.

~~~
shanemhansen
Is there anything about this proposal that would have been surprising to
someone 5 years ago? Anything to show for waiting most of the decade other
than a decade lost?

~~~
infogulch
Judging past work by the "obviousness" of the final solution is shallow,
juvenile, and dismissive. Every problem is easy when viewed through the lens
of of a nearly-complete solution. There have been a wide variety of
_published_ proposals for generics in Go [1], each of which seemed plausible
but had some insufficiency. Who knows how many proposals were conceived but
never left the developer's machine.

If it's so damn obvious/you're so brilliant where's _your_ proposal dated Jun
2010 (your 'decade lost') that "solves" generics?

[1]:
[https://github.com/golang/go/issues/15292](https://github.com/golang/go/issues/15292)

------
jeffdavis
There's a tension in languages between making it easier to write many layers
of libraries versus making it easier to write applications.

Languages that want libraries that go ten layers deep need to carefully manage
things with sophisticated type systems and compilers that can cut through and
inline all the abstractions so it still runs fast. Think rust, C++, haskell
(all of which, not by coincidence, have long compile times).

Languages for writing applications and solving problems tend to have a flatter
dependency tree and wider standard library (often including practical
functionality like JSON parsers). Think erlang, Perl, PHP.

I used to think Go was in the latter category, but it seems to be getting
pulled into the former category. Perhaps that's a reflection of Go developers
using more layers of abstraction than they have in the past?

~~~
politician
Go benefits from a continuous flattening phenomenon due to how interfaces and
types interact. The determination of whether a type implements an interface is
done at the interface use site rather than the type declaration site (as in
C#, Java, etc.)

I don't think Go developers are using more layers of abstraction than they
have in the past; they aren't smarter or more sophisticated than they were
before -- RAFT was written in Go, for example.

Your observation is a good one, however; are there other ways that Go
idiomatically flattens type hierarchies?

~~~
jeffdavis
"they aren't smarter or more sophisticated than they were before"

To be clear, I was not suggesting that more layers of abstraction reflect more
sophisticated _developers_. Just that more layers require something in the
language to help developers manage those layers without going insane.

There's a lot of wisdom in choosing a flatter approach when it makes sense.

~~~
politician
In your opinion, what languages do have effective tools for managing high
towers of abstraction? I'm genuinely curious, having spent many nights
building ontologies.

~~~
jeffdavis
C++ and Rust come to mind. C++ can certainly go awry, but it does seem to have
the tools available, even if they might be hard to use. Rust still has a few
things to prove, but things are looking fairly good. Both languages can
express constraints (e.g. type constraints) so that you don't have to make too
many assumptions, and you know when APIs change in subtle ways. And both
languages use inlining heavily to get "zero cost abstractions".

I'll include ruby as well. It doesn't have a lot of "tools" per se, but if
you're working on a nice codebase, it works out well even if using a lot of
gems. Part of this is probably the unit testing/mocking, and part of it is
that the code is just smaller. It does _nothing_ to help you with performance
though, so it can get really bad.

My experience with Java has not been great. I'm unconvinced that inheritance
hierarchies are a good idea at all, or much of the other ceremony around Java-
style OOP. Java uses JIT compilation, which can help with performance a lot.

If by "ontology" you mean "hierarchy", I'd caution you against that approach.
Hierarchies enforce an order that is just not there in many cases. For
instance, if you have an iPhone, would that be Hardware/Apple/iPhone or
Apple/Hardware/iPhone? An iPhone is both Apple and Hardware, but there is no
real order to those two -- yet if you try to put them in a hierarchy, then you
must choose (and the choice _will_ matter). I think interfaces/traits are much
better. Both Go and Rust got this right.

~~~
politician
Good insights. Sorry, when I said "ontology", I was referring to the Web
Ontology Language (OWL).

------
djhworld
Neat, thrown together Set implementation
[https://go2goplay.golang.org/p/FffAhV8aLyN](https://go2goplay.golang.org/p/FffAhV8aLyN)

~~~
coder543
Might as well use the zero-size struct{} instead of bool, given that you’re
not currently using the bool values for anything:
[https://go2goplay.golang.org/p/9iegVQ2VQCr](https://go2goplay.golang.org/p/9iegVQ2VQCr)

Alternatively, use the bool values and rely on the zero value for missing
elements:
[https://go2goplay.golang.org/p/E7yHQqAPseG](https://go2goplay.golang.org/p/E7yHQqAPseG)

~~~
bradfitz
And variadic args to NewSet looks a bit nicer yet at the call site:
[https://go2goplay.golang.org/p/fTp3W9IwuVP](https://go2goplay.golang.org/p/fTp3W9IwuVP)

~~~
parhamn
Is there a way to do this without losing range?

~~~
coder543
The proposal is, for better or worse, quite clear about not providing operator
overloading or anything similar in the near future.

You could always offer a method that returns a slice of unique elements, which
could then be ranged over.

~~~
djur
I don't think there's nearly as much of an appetite for operator overloading
as there is for an iterator protocol for range. That isn't to say that the
latter wouldn't have a lot to argue over (internal vs. external, etc.) but I
think the general usefulness of such a thing would be widely accepted by the
community once generics are a settled matter, while I doubt that will ever
happen for operator overloading.

------
pjmlp
Currently I just want some form of genericity, regardless which one, but I
fear we will go through yet another cycle of community feedback and by August
2021 still not there.

However not to be just negative, I will certainly take the opportunity to
provide my positive feedback as well, so looking forward to play with go2go.

~~~
zenhack
I'm sympathetic to being somewhat impatient; I'd really like to have generics
in Go years ago too. But I'm also glad they didn't just plow ahead with the
first draft of the proposal that came out; the way contracts worked was
horrible, and the slow deliberative process means that we don't actually have
to deal with those mistakes. Unfortunately, good design takes time.

~~~
throwaway894345
Me too. A bad generics implementation is profoundly worse than no
implementation at all. This angers people who have minimal experience with Go
and it certainly seems counterintuitive until you understand that the way the
community writes their code affects your experience whether it’s just a
different paradigm or a conflagration of paradigms, whether it’s a little
abstraction or gratuitous abstraction, etc.

------
renewiltord
Prefer no generics in Go. Like Go simplicity. Don't want all languages to
merge into single grey good form. Want strong trade-offs made.

~~~
shpongled
Why use many words when few do?

~~~
Strom
For one, everyone isn't fluent in English. I guess it could also be a low
bandwidth input situation.

~~~
lanius
I'm pretty sure shpongled is just quoting Kevin from The Office:
[https://www.youtube.com/watch?v=VvPaEsuz-
tY](https://www.youtube.com/watch?v=VvPaEsuz-tY)

~~~
ccktlmazeltov
longer version:
[https://www.youtube.com/watch?v=_K-L9uhsBLM](https://www.youtube.com/watch?v=_K-L9uhsBLM)

------
pansa2
It looks like this will make Go a significantly more complex language.

Despite Go's success over the last 10+ years, have its designers concluded
that the language is _too simple_? Is there no place for a simple statically-
typed language like current versions of Go?

If not, then for those of us who particularly want a simple language, is
dynamic typing the only option?

~~~
djur
This is a very conservative, simple implementation of generics. The vast
majority of its impact will be to make the code devs deal with on a day-to-day
basis simpler to use. It will reduce the necessity for parts of Go that are
already complicated, like go gen and reflection.

------
rowanseymour
The only time I get frustrated by the lack of generics in go in trying to get
keys from a map... I can't have a single `keys(map[string]T) []string`
function.

~~~
vlads_
Well, you can :)
[https://play.golang.org/p/sum21uSy1Ae](https://play.golang.org/p/sum21uSy1Ae)

~~~
grey-area
For certain values of can.

~~~
vlads_
Never said it was a good idea. It's inefficient, unsafe and hard to read but,
as far as I can tell any generic function with concrete return types can be
written using reflect (and any generic function with generic return types can
be written also, it just requires the caller to manually cast from an
interface{} to the correct type).

So, that's an interesting thing.

------
cybervasi
Ada language had generics since the first 1983 standard. When C++ came about
it introduced OOP features that Ada lacked and didn't get for another 12 years
when 1995 standard was introduced. C++ users never missed generics for many
years and the language became ubiquitous while Ada remained marginalized.
What's interesting is that all strongly typed languages have now jumped on the
generics bandwagon, which to me shows that being ahead of its time doesn't pay
off.

~~~
erik_seaberg
The first edition of Stroustrup is the only version without templates (and
exceptions). I remember awful macro workarounds for C++ compilers with broken
template support, so I think everyone knew that some kind of generics were
obviously needed. Ada got this and a few other things right, but it was
painfully verbose and the first compilers were so expensive that few seriously
evaluated it.

~~~
erik_seaberg
If I had to pick a language that got nearly everything right and shouldn't
have failed, it'd be Eiffel. But it was also priced more like a truck than a
tool until too late (at a time when programmers didn't generally cost six
figures!)

------
galkk
Typical to Go verbose style, but even that is better than nothing (example
from the doc)

    
    
        s := []int{1, 2, 3}
        floats := slices.Map(s, func(i int) float64 { return float64(i) })
    
    
        Type inference for generic function arguments
        This is a feature we are not suggesting now, but could consider for later versions of the language.

------
ccktlmazeltov
This is really sad, generics add a ton of complexity and get abused over and
over in many codebases. Sure if you have a competent team then you can stop
the bleeding, but it'll be all over the place in your dependencies.

Every developer who discovers generics has this urge to be clever and write
things that are hard to read and maintain.

Reading a Go codebase on the other hand is really a pleasure, this is due to
the simplicity and straight-to-the-point aspect of the language, as well as
the compile times. I really think Go had found a no-bullshit niche that it
catered really well to, and I'm scared of what Go post-generics will look
like.

Are there any other languages like Go that have no plans to move to generics?

PS: beautiful quote from rob pike:
[https://news.ycombinator.com/item?id=6821389](https://news.ycombinator.com/item?id=6821389)

~~~
typon
"My late friend Alain Fournier once told me that he considered the lowest form
of academic work to be taxonomy"

That's really unfortunate. I think a lot of the point of science (maybe even
the entire point?) is to model the world and create taxonomies. Instead of
being considered "busy work" or tedious, it should be held in the highest
regard. Similar reasoning is why I think Rob Pike's opinion on generics is
exactly wrong.

~~~
djur
I actually had never read that Rob Pike quote and it's really baffling to me.
It's not about generics at all! It's hard not to read it as saying "generics
are recommended to me by the same weirdos who like inheritance, and I don't
like inheritance, so generics are BS too".

------
baby
This saddens me :( as a reviewer of code I found Golang to be extremely easy
to read due to the lack of expressiveness. The most secure codebases I've seen
are all in Golang (or are written by strong engineers).

------
hashamali
While I’ve learned to work without generics, this is a welcome proposal. Many
cases where we’ve been forced to use interface{} + cast could be genercized.

------
shp0ngle
Is there a reason Go is not using <> instead of ()?

So instead of

func Print(type T)(s []T) { for _, v := range s { fmt.Print(v) } }

we would get

func Print<T>(s []T) { for _, v := range s { fmt.Print(v) } }

?

~~~
masklinn
The angle brackets increase parsing complexity because `>` is a valid
standalone operator (which neither ] nor ) is, usually) making the grammar
ambiguous: given

    
    
        Foo<Bar>
    

You still don’t know whether this is a bunch of comparisons or a parametric
type, meaning you need either feedback from the type checker into the parser
or possibly unbounded lookahead before you can even generate a parse tree.

------
kerkeslager
The anti-generics folks in the Go community are pretty prevalent still--
meanwhile the Go community frequently lauds Go's strong type system. I suppose
it's _possible_ that these aren't the same people, but it seems more likely to
me that many Go users don't know the difference between strong types and
static types, and think that all static types are strong types.

Without generics, Go _does not_ have a strong type system. It has a type
system that verifies just enough to force you to work around it to do things
that require generics, meaning that you don't get the benefits of verification
because you've worked around it. You get the development speed of a
statically-typed language with the type guarantees of a weakly-typed language:
the worst of both worlds.

------
rosshemsley
A litmus test I'm excited about is writing a less clunky linear algebra /
numerical optimisation package.

Gonum is good, but it can feel like a lot of the powerful abstractions in
numpy and Eigen are difficult to replicate.

Whilst numpy and Eigen do "turn the magic up to 11" just a bit too much for my
liking, I do like the idea of things like this:

    
    
        interface Scalar() {
            type float float32 int int32 int64
        }
    
        type DenseMatrix(type T Scalar) [][]T
    
        func Identity(type T Scalar)(int size) DenseMatrix(T)
    
    
    

etc.

------
mkchoi212
> Second, we know that many people have said that Go needs generics, but we
> don’t necessarily know exactly what that means.

Seems like the go authors are still bent on the idea that generics are really
not necessarily and that there is no such problem that can only be solved by
generics. Then is the generic drafting only a thing because they have been
pressured into it by the community? I wonder why Rob Pike one day decided that
it’s okay to change a language he almost vowed to never change after the
initial release.

~~~
woah
What’s funny is that the stdlib makes extensive use of generics

~~~
rmdashrfstar
"For me and not for thee"

------
kodablah
This is going to give rise to a utility-belt of sorts, which is what I'm
really looking forward to. There are many collections and common utilities
that we've all been hand-writing for a while (e.g. waiting for Set(string)
instead of map[string]struct{}, or a TreeMap(string, Thing) instead of
maintaining sort order separate or using reflection for an existing lib).

> Methods may not take additional type arguments

While I can fathom the complications that arise, I'm hoping they can work past
this restriction by launch.

------
andy_ppp
I wish they would just add macros instead and then you can effectively code
generate at compile time any specific generic-ness that is needed.

~~~
zenhack
Macros are not a substitute for parametric polymorphism ("generics"). With
lisp-style macros you could easily implement something like C++ templates, but
that's different in at least one critical way: with C++ templates, the
templates are expanded _before_ typechecking, whereas with proper generics,
typechecking happens while the type parameters are still opaque. The former
has several disadvantages:

\- It gets you really garbage error messages, because the type errors are
usually about something in the body of the template, wherever it tries to do
something that the type you substituted doesn't support, rather at the
offending call site.

\- It hurts compile times (above and beyond proper generics), since you need
to type check the generic function at every call site, rather than just once.

\- It makes it easy to break interfaces, because exactly what is required of a
type parameter isn't written down anywhere -- it's just whatever the body
tries to do with it.

(Though it is also true that generics are certainly not a full substitute for
macros. I would welcome some mechanism for doing codegen that didn't
complicate the build system and was a bit lighter weight than what we have
now).

~~~
gpderetta
C++ templates are not expanded before type checking. Template instantiation is
type driven. In fact you can even conditionally expand a template on whether
an arbitrary expression typechecks. You wouldn't be able to do it if templates
were a different phase.

Only early extremely non conforming compilers had macro-like template
expansion.

Edit: although calling templates type level macros wouldn't be completely
wrong.

Edit2: a better definition is that templates are type level functions: they
take compile time values, types and other type functions and return values,
types and type level functions.

~~~
zenhack
Fair enough; I suppose I was oversimplifying. I think the broader point I was
trying to make (in addition to the specific downsides I mentioned) still holds
though.

The Template Haskell paper has some interesting things to say on the nature of
C++ templates:

[https://www.microsoft.com/en-us/research/wp-
content/uploads/...](https://www.microsoft.com/en-us/research/wp-
content/uploads/2016/02/meta-haskell.pdf)

------
hitekker
Rob Pike is not mentioned in the blogpost or in the sources the authors
acknowledge as contributors to this project.

What is he up to nowadays?

~~~
ainar-g
He still commits code and responds to Github issues occasionally, and I think
it's safe to assume that he's still participating in the internal discussions
about the design. But most decisions about language changes these days are
made by Russ Cox, Ian Lance Taylor, and Robert Griesemer. At least, that's the
impression I'm getting.

~~~
hitekker
That jives with this article I found:

[https://evrone.com/rob-pike-interview](https://evrone.com/rob-pike-interview)

> Rob: Although it's far from certain, after over a decade of work it looks
> like a design for parametric polymorphism, what is colloquially but
> misleadingly called generics, is coming in the next year or two. It was a
> very hard problem to find a design that works within the existing language
> and feels as if it belongs, but Ian Taylor invested a phenomenal amount of
> energy into the problem and it looks like the answer is now in reach.

Sounds like he took a welcomed step back. Hopefully the new leaders can
deliver.

~~~
grey-area
Ian Lance Taylor has been involved in the language for a long time, since
2008. Here he is talking about the built-in function append and why it
shouldn't really exist in 2017

[https://www.airs.com/blog/archives/559](https://www.airs.com/blog/archives/559)

------
kardianos
The draft design document is very readable too:
[https://go.googlesource.com/proposal/+/refs/heads/master/des...](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-
type-parameters.md)

I'm rather excited..

------
anonfunction
I like this, here's a Sum function that supports all number types:

[https://go2goplay.golang.org/p/hPDnvRa-
Bif](https://go2goplay.golang.org/p/hPDnvRa-Bif)

------
suzuki
Now the abstract sequence type of "LINQ in Go"
[https://github.com/nukata/linq-in-go](https://github.com/nukata/linq-in-go)
can be written as

    
    
      type Enumerator(type T) func(yield func(element T))
    

and the "Select" method can be written as

    
    
      func (loop Enumerator(T)) Select(f func(T) T) Enumerator(T) {
            return func(yield func(T)) {
                    loop(func(element T) {
                            value := f(element)
                            yield(value)
                    })
            }
      }
    

You can call this method with type-safety as follows. Yay!

    
    
      func main() {
            squares := Range(1, 3).Select(func(x int) int { return x * x })
            squares(func(num int) {
                    fmt.Println(num)
            })
      }
      // Output:
      // 1
      // 4
      // 9
    

See
[https://go2goplay.golang.org/p/b0ugT68QAy2](https://go2goplay.golang.org/p/b0ugT68QAy2)
for the complete code.

And, for generality, you should write the method actually as follows.

    
    
      func (loop Enumerator(T)) Select(type R)(f func(T) R) Enumerator(R) {
            return func(yield func(R)) {
                    loop(func(element T) {
                            value := f(element)
                            yield(value)
                    })
            }
      }
    

However, you will get the error message then:

    
    
      type checking failed for main
      prog.go2:17:33: methods cannot have type parameters
    

According to
[https://go.googlesource.com/proposal/+/refs/heads/master/des...](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-
type-parameters.md#methods-may-not-take-additional-type-arguments) this seems
an intended restriction:

> Although methods of a generic type may use the type's parameters, methods
> may not themselves have additional type parameters. Where it would be useful
> to add type arguments to a method, people will have to write a suitably
> parameterized top-level function.

> This is not a fundamental restriction but it complicates the language
> specification and the implementation.

For now, we have to write it as
[https://go2goplay.golang.org/p/mGOx3SWiFXq](https://go2goplay.golang.org/p/mGOx3SWiFXq)
and I feel it rather inelegant. _Good grief_

------
whateveracct
The "we still can't abstract over zero values" section is only really solvable
with ad hoc polymorphism (type classes)

------
shp0ngle
Can `Option(type Value)` and some sort of `option.Unwrap` now totally replace
`(value, error)` and `err != nil` checks? :D

------
meddlepal
What a joke. Go devs spent a decade telling people they were waiting for the
right design for Generics to emerge. Finally after a decade they deliver to
us... Java-lite generics which are going to require the community to do a
massive library ecosystem retooling for next five to ten years similar to how
Java felt going from pre 1.5 to 1.5 and beyond.

This language is such a frustrating mess at times.

~~~
atombender
I'm not sure what you expected. With the exception of Ian Lance Taylor, the Go
team has expressed skepticism (and, in the case of Rob Pike, outright disdain)
about generics since day one.

To anyone who has followed the Go development process at all, this proposal
will not be a surprise. The previous proposal was also extremely conservative.
Personally, I don't see any other way. Going full-blown generics in the style
of Rust/Haskell/OCaml, for example, would drastically alter the language.

This is closer in scope to, say, Modula-3.

~~~
meddlepal
I'm more frustrated that it took a decade to get to this point. Not that it is
not more powerful.

------
nemothekid
I've been writing a lot of Rust lately after writing Go for 7 years now
(unrelated, but that is incredibly odd for me to write out. It still feels
like that funny new language that came out of Google). I've always said that
generics is overhyped (in Go), but I find that I write a lot of generic stuff
in Rust that is somewhat surprising. For example we have a JSON API that
returns data in the format '{"ok": bool, "data":...}'. In Go I might do
something with json.RawMessage and pay for decoding twice, or annotate every
struct with Ok/Data. But in Rust I can just have a type ApiResponse<T>. Then I
can have functions that operate just on ApiResponse, or ApiResponse<T=Users>.
This problem is solvable in Go, but in a different way and with less
guarantees. However that power comes at a mental cost, that creeps in C++ as
well. I spent more time playing type system golf trying to come up with the
optimal type for whatever usecase. In Go I might just "do the work", but in
Rust I've turned 5 minute functions into 30 minute api design thought
exercises. The jury is out if thats is a good thing or a bad thing.

That said, the only feature I'd steal from Rust is sum types and getting rid
of null. `nil` interfaces are the only language feature to actually cost me
money and Option<T> is just better in every aspect, and Result<T> is much
better than err == nil. I'd be perfectly happy with compiler blessed Option
and Result types even if the larger language didn't support generics.

~~~
vlunkr
Do we have a name yet for the internet phenomenon where every time Go is
discussed, Rust must be immediately brought into the conversation, and vice
versa?

~~~
munificent
It shouldn't be that surprising. Both are "low-level" languages that came onto
the scene around the same time, are highly opinionated, but stake out very
different points on the design space.

If you start talking about changing the recipe for Coca-Cola, surely Pepsi is
going to enter the discussion at some point, right?

~~~
gver10
This doesn't explain the obsession with Rust.

Fitting analogy: If there's an article about McDonald's, people would always
talk about Wendy's instead. No one would mention BurgerKing (C++), Subway (D),
KFC, ...

~~~
nilkn
I wouldn’t say that I’m obsessed with Rust. However, it’s the sort of language
I’ve wanted essentially since I started programming: a Haskell/OCaml-like
language with the sheer performance and practicality of C++.

I’ve been a long-time user of Haskell, but I can safely say Rust has
supplanted it almost entirely for me at this point. I never would have felt
comfortable betting on Haskell in a commercial environment. Rust? Absolutely.

~~~
hopia
Can you expand on why you would not bet on Haskell for commercial work? What
are those things that make Rust suitable for it but not Haskell in your
opinion?

------
dokka
I won't use GO until they add generics. Hopefully they'll add them someday. I
can wait as long as it takes.

------
albeva
I feel like Go lost its chance by being way too slow to innovate. From my
circle of people who I remember were extremely excited about Go when it first
came out - no one really uses it anymore. In our circle it has become a "meh"
language. Heck Apple Swift on server is way more exciting than Go these days.

~~~
zerotolerance
It isn't supposed to be an exciting language. It specifically not fancy. Great
for teams. Forced style consistency. Clear expectations. Difficult to mis-read
compared to most others. Terse, almost rude.

~~~
enahs-sf
There is a lot of beauty in go’s simplicity. Very straightforward to reason
about what’s going on.

I find it much easier on the eyes than java.

