Live data from Hacker News

Go: Support for Generic Methods

github.com

231–240 of 288 posts

Re: Go: Support for Generic Methods

#231
post #169

Earlier quoted context omitted.

Because they refuse to do something until they figure out how to do it well. I respect that.

Go's generics design is the most clunky one among popular languages.

Exactly. We heard for years they wouldn't do generics it until they could do it right, and that was perfectly fine with me. Who wouldn't want a well thought out implementation? Then they released generics and it was like, "this is what you thought was the right way to do it?!"

Re: Go: Support for Generic Methods

#232
post #169

Earlier quoted context omitted.

Because they refuse to do something until they figure out how to do it well. I respect that.

Go's generics design is the most clunky one among popular languages.

What’s the approach they should have used instead and how would it be better? Especially in terms of keeping fast compile times and overall performance.

Re: Go: Support for Generic Methods

#234
post #213

Since they can't implement interfaces, Generic methods are just syntax sugar for generic functions. I'm surprised they actually accepted this proposal for sugar.

Yes, the sugar is just to make chain calls with parameter types possible. The sugar reflects the limitation of the basic of Go generics design. Now they would make the language even more complex for such a small need. In facts, there are more problems in Go generics need to be solved earlier than this: https://go101.org/generics/888-the-status-quo-of-go-custom-g...

Thanks for sharing. I had no idea Go's generics had this many limitations.

Re: Go: Support for Generic Methods

#235
post #202
post #112

Earlier quoted context omitted.

Isn't an array of arrays by definition the sequential implementation? Otherwise you would have an array of pointers to arrays. The usage (syntax) for them would be the same but the performance would not be. They also have different uses. You would expect an array of arrays to be an array of arrays which share the same length. For an array of pointers to an array you would expect dynamic length arrays contained within…

> Even in c++ could you not just define some int [1000][1000]foo? If it fits on the stack, yes. Typical code using MD-arrays is scientific code, and the data they manipulate generally do not fit there.

Would the compiler not allocate the memory contiguously on the heap in that case then? Seems like a reasonable thing to do.

Re: Go: Support for Generic Methods

#236

Earlier quoted context omitted.

It's (sadly) still not possible to express monads with this change, since generic methods can't implement interfaces. You'd probably want something like: type Monad[T any] interface { Bind[U any](func(T) Monad[U]) } However this requires the Bind method to be generic, which still isn't allowed in an interface

I am not very familiar with Go and especially not its generics support. Can you implement the "join" version instead of the "bind" version, where you turn a T[T[a]] into a T[a]?

Hmm I wasn't familiar with join, but it looks like you still need join + fmap for the construction? I believe fmap would also need a generic method

Re: Go: Support for Generic Methods

#237

Earlier quoted context omitted.

Gophers are usually quite fast, perhaps an elderly turtle would be a better mascot?

> Gophers are usually quite fast, perhaps an elderly turtle would be a better mascot? The slow turtle wins the race against the overly eager rabbit... so I'm okay with that

The lesson of the parable of the tortoise and the hare isn't that "slow beats eager", it's a lesson against unfounded overconfidence in one's own abilities.

Re: Go: Support for Generic Methods

#238
post #91

Earlier quoted context omitted.

We care that time and time again, when anyone ever brings up a criticism of the language, they’re told that everything is just fine and it’s not a problem and we just don’t get the Go Philosophy. There’s not a problem, stop trying to make Go like every other language, and changing things would make the language more complicated and worse. Then when the language is inevitably changed for the better, resolving the comp…

Yeah, I worked with a guy in the late 2010s - one of the most painful people I've ever worked with - who would tell anyone that would listen that Go (as it was in 2018) was the perfect programming language - it had all the features you'd ever need - no more, no less. It doesn't need generics, the package management story is fine etc. Thankfully he's been out of my life for a long time now but I believe he's still wri…

This is an outlier. The Go team and community never endorsed that. In fact, their position has always been the opposite. To give just one example, see [1].

[1]: https://research.swtch.com/dogma

Re: Go: Support for Generic Methods

#239
post #148

> Go doesn't support such generic interface methods because we don't know how to implement (calls of) them, or at least we don't know how to implement them efficiently. I don't really understand this argument. I read the discussion linked to[1], and yeah, monomorphization approaches (whether at compile time, link time, or runtime with JIT) are obviously going to be difficult or impossible, but the reason against usin…

> but the reason against using runtime reflection is mostly that it's slow. More specifically, it is that it would introduce surprising performance cliffs – code becoming surprisingly slow due to seemingly unrelated changes. Though BTQH I think an even more important argument is that you would need to have effectively two generics implementations, one working at runtime and one working at compile time. That's a lot o…

BTQH?

Re: Go: Support for Generic Methods

#240
post #148

> Go doesn't support such generic interface methods because we don't know how to implement (calls of) them, or at least we don't know how to implement them efficiently. I don't really understand this argument. I read the discussion linked to[1], and yeah, monomorphization approaches (whether at compile time, link time, or runtime with JIT) are obviously going to be difficult or impossible, but the reason against usin…

The real problem is that Go produces interface implementations dynamically.

The determination wether type T implements interface I is made at runtime. So is generation of the necessary vtables to produce the interface implementation.

So you can do things like this in package a:

  type S struct {
      //...
  }

  func (s \* S) Foo() {
      //..
  }
and something like this in package b:

  type Foo interface {
      Foo()
  }

  func DoSomethingWithAFoo(f Foo) {
  }
and something like this in package c:

  func Stuff(obj any) {
      theFoo, _ := obj.(b.Foo)
      theFoo.Foo()
  }
And then do:

  var s a.S
  c.Stuff(s)
And everything works.

For generic functions, go uses a strategy similar to C++ templates: when you call a generic function the compiler statically produces a concrete specialization of the generic function based on the inferred types for generic parameters.

That is, if you do:

  func Bar[T any](x T) {
    //...
  }
And you do:

  var x int
  var y string
  var z float64

  Bar(x)
  Bar(y)
  Bar(z)
The compiler statically generates 3 versions of Bar, one that takes an int, one that takes a string, and another that takes a float64.

These two things don't work well together. If I have a variable typed as `any`, and I want to cast that to an interface, I need to dynamically determine 2 things:

1. The shape of the interface's vtable. The go runtime does this by iterating over the runtime metadata for the interface type.

2. For each named method in the interface's vtable, the address of the concrete function to stick in that vtable slot. This is done by accessing the reflection metadata for the implementing type. It verfies the method with name X for type T matches the required signature for the method with name X for interface I, then sticks that method pointer into the appropriate vtable slot.

The problem, however, is what happens when the method with name X is generic. There may, or may not, be an actual concrete method for the set of type parameters. It's possible that statically type T does implement interface I (via generic methods) but that dynamically it doesn't because the particular generic instantiation needed for the particular interface was never made statically.

Prior to go 1.27, this was never an issue, because methods could not declare their own type parameters. They could reference the generic parameters of the receiver, but once the receiver type was known, there was only ever one concrete method X for that receiver.

Once you allow methods to have their own generic type parameters, the compiler can introduced several different concrete implementations for a method X.

This is ok, when you do somethnig like:

  var x SomethingWithGenericMethods
  x.Foo(1)
  x.Foo("hello")
  x.Foo(1.2)
Because the compiler knows statically from the Foo call sites which concrete methods it needs to generate.

But, when you introduce a dynamic cast:

  var x SomethingWithGenericMethods
  var i SomeInterface

  i = x.(any).(SomeInterface)
  i.Foo(1)
  i.Foo("Hello")
  i.Foo(1.2)
It's entirely possible that the necessary Foo implementations don't actually exist in the binary.

So, go 1.27 introduces generic methods, but it gets around this problem by saying:

1. Interface types can't define generic methods

2. Generic methods can't be used to implement interfaces

Thus, it allows adding generic methods without introducing the issues that crop up with dynamic interface implementations.

Post reply on HN