Type erasure has little to do with the power of generics at the language level. Haskell, with one of the most powerful type systems around, erases types to a greater extent than Java does (concrete types are still encoded in JVM bytecode, Haskell erases everything).
For example, by not allowing one to create a parametrized method. `func (self SomeNonGenericType) Foo[T any](param T) T` is not allowed, best one can do is `func Foo[T any](self SomeNonGenericType, param T) T`.
Why not use a free function? That is more flexible. Go isn’t Java, it is not a deadly sin to write free functions.
You look at something like the Reader and Writer interfaces and there are very few methods. The Go approach is to put more functionality in free functions such as fmt.Fprintf which allows more code reuse across multiple types.
In fact I view the heavy Java use of methods as an anti-pattern.
One surely can, and probably that's about the only option they have.
But for some `foo.Add(bar)` can be considered nicer than `AddFoo(foo, bar)` or `foopkg.Add(foo, bar)`. The semantic differences are mostly in in an aesthetic/stylistic/personal preferences realm, so it's hard to argue for or against it.
Except that in Java, the former is more common and in Go, the latter is more common. Really, the latter looks more like idiomatic Go, which makes the original complaint somewhat redundant. You might stylistically prefer the former, but then you're probably not going to enjoy using Go anyway.
Well, yes, name "Add" was a bad idea - collection management in particular tends to use free functions somewhat more - starting from the built-in `append`, hah. Although even that is not universal - consider e.g. github.com/deckarep/golang-set, which I believe is one of most popular set packages, based on number of imports. It doesn't strike me as non-idiomatic), it's all methods there and not free functions.
And either way, I'd disagree on the general principle. I'm not claiming to be well-versed in Go styles and patterns, but most well-designed libraries I've seen had exposed `foopkg.NewFoo()`, useful structs and constants (e.g. `foopkg.FooParams` or `foopkg.NoSuchFoo`) and the rest is typically interface methods.
Well, IIRC the strings package for example is pretty much all functions, and so is fmt (IIRC). You might not consider these packages “well-designed” but they are certainly idiomatic. I don’t have the inclination to go trawling through the standard library for other examples, but I will say from experience, as a former Java developer and now a Go dev, I’ve sometimes found Go‘s approach confusing because it’s more function oriented than OO.
So I guess our experiences differ, which of course is fine, but I respectfully don’t concede the point. :-)
You can certainly use generic parameters in methods—the [T any] just has to be on SomeNonGenericType, even if it's not used inside the type itself https://rakyll.org/generics-facilititators
Yes, but that's completely different thing. If you move T you would change the semantics.
- func (self SomeType[T]) Foo(...) is a method for a class parametrized on T. So if you want foo.Foo(1) then it only works for foo that's SomeType[int]. You cannot then easily call foo.Foo("one") on the same instance.
- func (self SomeType) Foo[T any](...) - if that'd be a thing - would be a generic method on SomeType that works for any T. So you can foo.Foo(1) and foo.Foo("one") on the very next line and that would work.
You can instantiate it with [any] then use whatever you'd like https://go.dev/play/p/JeSuB_xYNEf, but I don't think it would work with a type constraint such as `int|string`, which would be a fair point.
Sure, but it's no longer generic, you're just using runtime types again.
This is extremely important, because it means `func (someType[T any]) foo(T x) T`, `x.foo(1)` would return `any`, not `int` like a generic function would.
In fact, if you're going to instantiate a generic type parameter with `any`, I would very much doubt you wanted a generic type in the first place.
Oooh. My bad. Thank you! I honestly thought that wasn't possible at all. Today I learned.
However, it won't work for return values, right? Because e.g. `func (some something[T]) hi(b T) T` would return `any` and not whatever it was provided (`int` or `string` respectively).
Type erasure is interesting. It turns out that not doing type erasure (combined with instanceOf-like things, like pattern matches on type parameters) breaks parametricity. Parametricity is a very powerful reasoning tool.
Of course, type erasure isn't the actual culprit in that case, but once you don't do it, it gets very tempting to allow pattern matching on type parameters...
I read an essay by a CS post doc taking about type erasure. He said getting to the point where you can do type erasure is golden. And then everyone makes the mistake of actually implementing type erasure. He said you consign yourself to no tooling and terrible debugging.
You do realize that type erasure is the common way of dealing with (generic) types? Haskell also does type erasure, as well as basically every language outside C#.
Java is way worse than Go on that aspect.