Rendered at 16:28:47 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
thayne 15 hours ago [-]
> 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 using runtime reflection is mostly that it's slow. But that runtime reflection is exactly how you would work around it today.
For the Identity example, could the interface be compiled to be basically equivalent to:
Identity(any) any
and then at the callsite add a cast of the return type to T?
I suppose primative non-pointer types add a bit of a wrinkle but even if it generic methods was restricted to pointer types, that's better than nothing. And the number of those types is relatively small, so when the implementation is compiled it could just instantiate method implementations for all the primative types, if they apply, and then maybe remove them if they aren't needed at link time.
Of course it's also possible there is some detail I've missed.
> 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 of complexity, with surprising failure modes if these two are not bug-compatible.
> But that runtime reflection is exactly how you would work around it today.
I think the overwhelming majority of people will "work around it" by just not trying to use generic methods.
thayne 9 hours ago [-]
> you would need to have effectively two generics implementations, one working at runtime and one working at compile time
My understanding is that go already has a hybrid system works at compiletime and sometimes at runtime.
Merovius 7 hours ago [-]
I'm not sure what you mean. Perhaps you are referring to the reflect package? In that case, yes, that exists. But it is limited in its power (for example, it doesn't allow to create types with methods – precisely because of the difficulties we are talking about) and a comparatively frequent source of bugs. If anything, it provides pretty strong evidence for the problems with this approach.
My point is for interface generics it could just always use a single instantiation. Similar to what java does.
Or alternatively, go could go the other direction and add a new type of interface that is only for use in generic constraints, and then generic methods could be part of that interface, but not normal interfaces, so that the generic methods could be called from other generic functions. That would be similar to rust and c++.
Boxxed 3 hours ago [-]
BTQH?
Falell 2 hours ago [-]
Maybe a typo for To Be Quite Honest?
whaleofatw2022 13 hours ago [-]
> Of course it's also possible there is some detail I've missed.
Can't speak too deeply for Go specifically, but I do know on .NET one of the big reasons generic methods where T is a structure gets monomorphized per type, is so that stack size is adjusted and potentially even arg passing (i.e. large struct) as far as the caller/callee.
swisniewski 2 hours ago [-]
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() {
//..
}
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.
MrBuddyCasino 10 hours ago [-]
Like Java‘s generic erasure? Primitive types aren’t supported at all, have to used boxed ones. Its slightly annoying but not too much. You can fall back to arrays to have performant primitive containers.
pjmlp 11 hours ago [-]
Go becoming a proper 21st century language, is like pulling teeth.
It is Apple's school of design, think different, ah, actually, there are reasons why the fence is in the middle of nowhere.
Then the design ends up half way there versus being done properly from the beginning.
zerotolerance 1 hours ago [-]
Your comment could have been, "I've never agreed with the design philosophy behind Go." I've always appreciated the apparent Go design philosophy and feel like it most matches my lessons learned from 20 years in software. Feature minimalism is a feature for languages targeting organizations with thousands of programmers. If by 21st century language, you mean one that has become unrecognizable through multiple generations of fashionable feature and ecosystem thrash then I'm all for Go not becoming a 21st century language. Language should be boring if the target environment is large teams with varied skill sets. Plain speak. Low jumpy behavior. No cream, no syntactic sugar. Get the job done.
piekvorst 9 hours ago [-]
“Done properly from the beginning” means explaining why a particular feature is either included or not. In this sense, Go is done properly from the beginning. It would be wrong to add every popular feature uncritically.
pjmlp 9 hours ago [-]
"They are likely the two most difficult parts of any design for parametric polymorphism. In retrospect, we were biased too much by experience with C++ without concepts and Java generics. We would have been well-served to spend more time with CLU and C++ concepts earlier."
Yeah very critically.
piekvorst 9 hours ago [-]
You can’t be omniscient, I think.
pjmlp 9 hours ago [-]
It is sufficient to actually care about history of programming languages design, acknowledge the paths trailed before since FORTRAN came up in 1958, no need for omnisciency.
Less "we know better", more "actual history".
xena 23 hours ago [-]
This will finally let me make the monad library I've been dreaming of for years. Be afraid.
bonesss 22 hours ago [-]
A monad library in go can really on have one name… …
oh_fiddlesticks 21 hours ago [-]
Go nad or go home
digitaltrees 13 hours ago [-]
Underrated
whaleofatw2022 13 hours ago [-]
Strife
uproarchat 9 hours ago [-]
Weeee
hennilu 9 hours ago [-]
GoMad
throwaway894345 18 hours ago [-]
Mongo
WesolyKubeczek 4 hours ago [-]
(is appalled!)
zephen 18 hours ago [-]
Mo-go?
nasretdinov 22 hours ago [-]
We already have monads at home (return X, err)
dnnddidiej 17 hours ago [-]
Can we have Exception monads? Asking for friend.
AdieuToLogic 11 hours ago [-]
> Can we have Exception monads? Asking for friend.
This is nonsensical. Monads define a strict set of behaviors formalized as "monad laws"[0].
Perhaps what you want is a container which adheres to monad laws capable of abstracting exceptions. Two exemplars of same are Haskell's Data.Either[1] and Scala's Either[2].
I don't think it's nonsensical, it's just another name for the same thing. E.g. in the Haskell wiki it says, "the Error monad, also called the Exception monad".
> Perhaps what you want is a container which adheres to monad laws capable of abstracting exceptions
That is what I meant. Struggling to picture what the other "nonsensical" thing is.
assbuttbuttass 16 hours ago [-]
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
_jackdk_ 14 hours ago [-]
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]?
assbuttbuttass 4 hours ago [-]
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
9rx 12 hours ago [-]
Funnily enough, Go's generics were designed by the same guy who introduced monads to computer science. Everything comes fill circle.
AdieuToLogic 12 hours ago [-]
> Funnily enough, Go's generics were designed by the same guy who introduced monads to computer science.
No contributor to Go is responsible for "introducing monads to computer science", as the Monad concept is a member of (or defined by if you prefer) Category Theory[0].
As you point out, monads come from category theory, not native to computer science. Thus there had to be someone to introduce approaches to applying monads in computer science. The paper usually credited with that is: https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/b... Which the parent rightfully points out was written by the same person primarily responsible for the design of generics in Go: https://homepages.inf.ed.ac.uk/wadler/topics/go.html
throwway262515 7 hours ago [-]
On that note, calculus come from physics. AI should really hand back all those jacobians and hessians.
klik99 6 hours ago [-]
I remember lack of generics being pitched as a feature of Go initially, not a lack. The original design goal was simplicity. I don’t use Go, so have no opinion on this, just interesting that it’s going in this direction.
matthewmc3 5 hours ago [-]
Generics being omitted wasn't pitched as a feature that I ever saw. The discussion was mostly around "hic sunt dracones" with generics. The Go authors were very aware from the get-go that generics are an incredibly difficult thing to implement well, and the commitment was always to not do it until they were really ready to tackle it. The bizarre thing to me is that when they did tackle it, they left this massive implementation gap, and then what little they did implement didn't seem useful enough that anything meaningful was added to the standard library. So all this talk about generics for years was a big nothing-burger. That is, I hope, until 1.27 when this gets released. Flags is one in particular I think is in desperate need of proper generic methods.
nasretdinov 1 days ago [-]
Lack of generic methods was really surprising to me when I was first trying to use generics in Go. Nice to see it being actually implemented
ncruces 1 days ago [-]
To be replaced by the surprise when you figure out these methods don't implement interfaces.
Still, in this case, half the feature is better than none at all, IMO.
nasretdinov 1 days ago [-]
Generic interfaces are going to be implemented later too if I'm reading correctly. So no real surprises there :). I guess the only surprise yet is that generic interfaces aren't supported, so generic methods physically can't satisfy any interface
kbolino 20 hours ago [-]
Generic interfaces already exist. Generic interface methods, which would be relevant, are not planned. The reason is outlined early in the proposal: nobody knows how to implement them efficiently. Rust has the same problem, for what it's worth: dispatchable functions on dyn-compatible traits cannot be generic [1].
Or to look at that from another angle, if you were to define a Trait which has generic methods that Trait won't be "dyn-compatible" meaning that you can't do dynamic dispatch with this trait, which may be irrelevant to you (if you don't want dynamic dispatch anyway) or a showstopper (if you needed it, now your project won't compile).
dwattttt 18 hours ago [-]
That is another way of looking at it, but given the topic, you're gonna have to expand or contextualise that. I Rust a fair bit, and only barely follow.
tialaramex 16 hours ago [-]
Actually in hindsight I think my perspective was less helpful because you can write a dyn compatible trait with a generic method it's just that you can't call the method via the trait objects, dynamic dispatch isn't possible for your function. So the original way to think about it was superior.
Merovius 11 hours ago [-]
FWIW I found, so far, that bringing up dyn-compatibility to Rust people was very useful in helping them understand why Go's interfaces won't ever have generic methods.
The one additional piece of information you need is that in Go, all interfaces are supposed to be trait objects. The exception are union-elements, but that's really a restriction the Go team is trying to remove, not a model to base more features on.
EdwardDiego 16 hours ago [-]
Is it because of the kinda built-in duck typing, for want of a better word? The thing where if you have a method (or methods) that matches the signature of method(s) of an interface, you implement the interface without explicitly declaring so?
Merovius 10 hours ago [-]
More specifically, it is because of interface type assertions – the fact that if you have a value of some interface type (e.g. `any`), you can dynamically assert that it is another interface type (e.g. `io.Reader`). A good example of that is `io.Copy`: https://cs.opensource.google/go/go/+/refs/tags/go1.26.3:src/...
This aspect is what prevents you from statically knowing which interface-implementations you need to generate for a specific concrete type. There could always be new ones added at runtime.
9rx 16 hours ago [-]
> for want of a better word
Structural typing is the term typically used to describe "static duck typing".
catlifeonmars 12 hours ago [-]
I’d describe structural typing as a special case of duck typing FWIW.
9rx 12 hours ago [-]
I suppose. That special case is that it is evaluated statically, whereas duck typing is evaluated dynamically. That's the whole difference between them. They are otherwise identical concepts.
But who is to say that dynamic evaluation isn't the special case?
ncruces 23 hours ago [-]
I didn't see anything beyond "this doesn't prevent us from doing it" yet.
Did you?
18 hours ago [-]
h1fra 1 days ago [-]
slowly implementing all the things they said we didn't need
zarzavat 15 hours ago [-]
Watching Go's development is like reliving the development of Java (which also didn't have generics at first), but over decades instead of years. Cannot wait for Go to implement an error handling system in the 2030s.
20k 13 hours ago [-]
Its very funny watching certain segments of the programming industry rediscovering incredibly basic programming principles, after railing against them for so long. The AI people are starting to try and create formal specs to force the AI to generate an exact output, which is absolutely hilarious to me
Dynamically typed/untyped languages finding that strict and visible typing is actually good is another
jimbokun 12 hours ago [-]
I feel like there was a name for formal specs that produce an exact output, but it escapes me…
pjmlp 10 hours ago [-]
Another one, WebAssembly people recreating application servers, and networking object models.
pjmlp 10 hours ago [-]
Thankfully we already have Java, so using Go is really for the scenarios where it cannot be avoided.
CamouflagedKiwi 1 days ago [-]
They didn't say they never wanted to do generics, but that they did want to take their time and do them right.
Debatable how much they have been "right", although this gets them somewhat closer. And I think they have not been "wrong" in the ways they wanted to avoid (they referenced some issues with Java generics as prior art, although I forget the details).
tines 1 days ago [-]
From another commenter here:
> The post quotes the Go FAQ as saying, "we do not anticipate that Go will ever add generic methods".
jimbokun 12 hours ago [-]
> “What do you think? There was a man who had two sons. He went to the first and said, ‘Son, go and work today in the vineyard.’ “ ‘I will not,’ he answered, but later he changed his mind and went. “Then the father went to the other son and said the same thing. He answered, ‘I will, sir,’ but he did not go. “Which of the two did what his father wanted?” “The first,” they answered. Jesus said to them, “Truly I tell you, the tax collectors and the prostitutes and the Go language maintainers are entering the kingdom of God ahead of you.
But they haven’t added generic methods, really. This change just lets you use method syntax in cases where you could have used a function. It’s a pretty conservative change that I think a lot of people here are misunderstanding. Actual generic methods wouldn’t really make sense (for the same reason that you have similar restrictions on dynamic trait implementations in Rust).
skywhopper 1 days ago [-]
Is anyone actually mad about this, or do people just bring it up to stir the pot? Who cares what the FAQ says? They've worked out a way to add it easily in a backwards compatible way that can solve some problems. They had not identified this solution at the time they wrote the FAQ, and Go has been perfectly usable without this feature for 16 years.
tines 23 hours ago [-]
I'm not mad, I'm a proponent of stronger type systems. I'm just correcting the record about
> They didn't say they never wanted to do generics, but that they did want to take their time and do them right.
piekvorst 10 hours ago [-]
What’s the correction? The two claims are not in conflict. Saying “we don’t expect to ever add X” is not equivalent to “we never wanted to add X.” It simply means that they didn’t think it would happen, which can coexist with an underlying willingness to consider it if a suitable approach appeared.
stouset 23 hours ago [-]
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 complaint, suddenly it was always going to happen and it was just a matter of getting the details right.
Every other language community I can think of is more than willing to acknowledge the shortcomings of their language. “Yeah, this kind of sucks in principle but it’s not something that gets in the way in practice” is a fine perspective. So is “this was a tradeoff; we went in this direction and these are the resulting downsides”. But the golang community practically trips over themselves to constantly argue that obvious shortcomings in the language are actually a good thing and we just don’t get it.
Nobody is saying the language shouldn’t improve. We’ve all been begging the language to improve. But we’re also tired of the constant, obvious, and shameless gaslighting from the community whenever things do get better. You aren’t going to like the comparison, but it’s extremely Trumpian.
jamesfinlayson 16 hours ago [-]
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 writing Go, and I bet that he's telling anyone that will listen that Go (as it is in 2026) is the perfect programming language and that its implementation of generics was necessary and perfect etc.
He wasn't the only one but he certainly took it to the extreme.
piekvorst 3 hours ago [-]
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].
I think it’s pretty clear this post was a response to the clear dogma within the community.
> But we need help from everyone. Remember that none of the decisions in Go are infallible; they’re just our best attempts at the time we made them, not wisdom received on stone tablets.
2 hours ago [-]
the_gipsy 19 hours ago [-]
You just can never trust what the go team says.
Matl 18 hours ago [-]
Yeah, because software is not meant to evolve and people are not meant to either.
the_gipsy 18 hours ago [-]
You're not meant to gaslight about the evolution of software.
kunley 6 hours ago [-]
You're not meant to apply poor man's psychology everywhere just because you have heard about a certain psychological term.
array_key_first 16 hours ago [-]
Changing your mind is not gaslighting, people just change their mind sometimes.
the_gipsy 10 hours ago [-]
It's not this one thing, it's that yhey consistently do this
jaxlfkkgken 23 hours ago [-]
[flagged]
1 days ago [-]
thayne 23 hours ago [-]
Depends you who "they" is. If you mean the go development team, then yes, they said they wanted to "take their time and do them right"¹. But there are many "gophers" who did say that there was no need for generics, and that it shouldn't be added to the language.
¹ I would argue that it is really, really hard to add generics to a language after it has already matured, and still "do it right" than to add it in the beginning. At least if you care about backwards compatibility. Backwards compatibility adds a lot of constraints to your generics system that will almost certainly lead to a sub-optimal design. And you will be stuck with a standard library, and a lot of existing ecosystem code that would benefit from generics, but don't because generics didn't exist when they were written. This is a lesson I wish go had learned from Java's generics.
someone_19 24 hours ago [-]
I agree that they were clearly not in a hurry. I disagree that they are doing everything right. I am interested to see how they will fix the 'million dollars mistake'.
jimbokun 12 hours ago [-]
Because they refuse to do something until they figure out how to do it well.
I respect that.
tapirl 12 hours ago [-]
Go's generics design is the most clunky one among popular languages.
matthewmc3 4 hours ago [-]
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?!"
jimbokun 4 hours ago [-]
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.
tapirl 1 hours ago [-]
[dead]
TheChaplain 1 days ago [-]
It's not a bad thing to realize that one can be wrong and then strive for change.
a-french-anon 1 days ago [-]
Maybe, but personally I've become quite tired of programming languages "organically grown" as opposed to properly designed the first time. After a good decade of C then C++, I found ANSI CL (despite being a massive compromise and unfinished) much more coherent and complete than both.
bbkane 1 days ago [-]
I know Go is justly criticized for many of its design decisions, but it still feels well-designed and "small" to me in day to day usage when many other languages don't.
a-french-anon 1 days ago [-]
Eh, the thing with generics coming late is pretty much what I meant by "organically grown".
My best litmus test these days is support for multidimensional arrays because it's always needed at some point in general purpose languages. CL and Ada had it right from the start while C++ needed C++23/26 to get std::mdspan and we still need to wrap it to pass the underlying/owned memory pool around (https://rosettacode.org/wiki/Multi-dimensional_array for more).
nish__ 1 days ago [-]
Doesn't every language support multidimensional arrays? It's just an array of arrays, no? What am I missing?
umanwizard 24 hours ago [-]
An array of arrays is an extremely inefficient and error-prone way to represent multidimensional arrays.
If I want a 1000x1000 array, representing it physically as a single 1000000-element array requires one allocation, and processing it element-by-element (assuming it's stored in the same order we're iterating over it) is sequential in memory and therefore very efficient.
Representing it as 1000 separate 1000-element arrays requires 1000 allocations, and pointer-chasing every time we move from one row to the next.
j_w 22 hours ago [-]
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 the original array.
Even in c++ could you not just define some int [1000][1000]foo? I've never really used C++ but my C knowledge assumption is that is 1000000 continuous elements.
20k 13 hours ago [-]
The C++ way to do it currently would be:
std::array<std::array<T, N>, M> data;
Which is contiguous
int data[M][N];
also works fine and is contiguous in C++
Edit:
For the stack at least. On the heap, you'd need to use a single std::vector<int> and do the indices manually, or use mdspan
uecker 11 hours ago [-]
I does not work fine in C++ when N and M are not compile-time constants, which is basically always the case in any interesting numerical algorithm. Also not in Rust.
It works fine in C though, or FORTRAN, or Ada, or ALGOL 60, ...
pjmlp 6 hours ago [-]
Which is why std::mdspan exists, and std::linalg.
NVidia has pivoted to design CUDA hardware with focus on C++ back in , and seems to be doing quite well for them.
They were also the ones sponsoring the ISO work on mdspan, while HPC research labs are pushing for linalg on top.
I would rather be using Ada today, but that isn't how the world moves.
shakow 9 hours ago [-]
> 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.
j_w 4 hours ago [-]
Would the compiler not allocate the memory contiguously on the heap in that case then? Seems like a reasonable thing to do.
nish__ 23 hours ago [-]
I see. That makes sense.
xscott 1 days ago [-]
Scheme is (or at least was) coherent. You don't need to look any further than set/setf/setq to see that Common Lisp is "organically grown" from the fertilizer of a committee. CL does its best to make every other lisp more attractive.
rootnod3 1 days ago [-]
Which Scheme are we talking about? R5RS? R7RS-small? R6RS? With SRFIs? Without? Which scheme? Is it `(library...)` or `(define-module...)`?
xscott 1 days ago [-]
Heh, I'd probably take R4RS with define-syntax :-)
rootnod3 23 hours ago [-]
I mean, good choice, but you see the point, right?
As much as ANSI CL has it's flaws, it has a standard, as much of a mixed bag it might be. Scheme is just a general potpourri of "we kinda have a guideline, but do whatever".
I would very much prefer scheme if the different implementations had a working standard. But I can't take my Chez-scheme code and throw it into Guile-scheme.
But pretty good chance I can take my ECL code and throw it into SBCL or LispWorks.
xscott 18 hours ago [-]
> you see the point, right?
Bah, I think this debate was already old when I first saw people arguing it on comp.lang.lisp in the 90s. I don't have a dog in this fight other than to reject the notion that Common Lisp is "coherent" and not "organically grown".
The original Scheme belongs in the category of languages like Standard ML and SmallTalk, where a small, careful, and talented group designed them with focus. Common Lisp seems like a bunch of smart people with competing interest and legacy baselines tried to meet in the middle. To the extent CL is more pragmatic, it's another example of "Worse is Better".
rahen 1 days ago [-]
Scheme has a coherent and minimalist design, but its ecosystem and abstraction facilities feel too sparse for large applications.
When I started building a Lisp-based machine learning framework, Guile seemed like the right choice because it provides GOOPS and generic functions, yet I still ended up with a lot of boilerplate to compensate for the lack of a strong type system.
Scheme feels to me like C is to C++: not ergonomic for large-scale application development. Go is one of those languages that has both minimalism and productivity.
a-french-anon 8 hours ago [-]
Common Lisp didn't grow into its warts, though, it was intentionally a "Common" Lisp. A compromise, like I wrote.
pizza234 1 days ago [-]
It isn't realistic to expect a design to be "proper in first place" because requirements change; my opinion is indeed the opposite - I find it natural for programming languages to have a (sort of) lifespan, and for new ones to (sort of) take their place.
array_key_first 16 hours ago [-]
Sure but literally everyone and their mom said these features were needed and then Go team said "nuh uh!!!" But, as it turns out, they are needed because they solve real problems, and are not just fake complexity like some people strawman.
Hopefully next they can add some error handling syntax and controls.
someone_19 1 days ago [-]
Indeed, in 2012, it was not clear to anyone that generics were needed /s
ndr 1 days ago [-]
"Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp."
-- Greenspun's tenth rule
He had some lack of conviction to scope it so narrowly.
ramon156 1 days ago [-]
So which language had it right from the start? is there a language that has a very low rewrite status?
poncho_romero 1 days ago [-]
I think Elixir is a good candidate here. It's small, coherent, and composes well, and (at least to my understanding) the authors consider the language finished, with no new major features planned.
sunsetSamurai 13 hours ago [-]
Elixir is missing static types though, it's hard to go back to work on dynamic languages.
I'd particularly like examples of statically typed languages that "got it right" (since I love me my types)
galangalalgol 1 days ago [-]
Ocaml maybe? Multi threading didn't seem necessary and introduced the possibility of data races.
maccard 1 days ago [-]
That’s whataboutism - no language is perfect, but given when go released it’s fair to hold them to a higher standard than languages what were designed 25 years earlier.
As an aside - D, Zig, Rust, even typescript got most of the lessons learned from C right
blanched 1 days ago [-]
I'm not familiar with D, but Zig and Rust are well-known for continuously evolving.
And besides Rust's high count of RFCs, there are things like async (I'm not complaining about it, but its an obvious large-scale "change"), module system changes, etc.
(To be clear, I like both languages a lot. But I wouldn't call them slow moving or right from the start.)
Maxatar 1 days ago [-]
D literally can't even maintain backwards compatibility between minor version updates not to mention a big part of the D community left when D reinvented itself with D2. Among languages it's probably the one that is constantly in a state of flux.
sunsetSamurai 13 hours ago [-]
what's a big project built with D? I feel it gets mentioned a lot on hackernews but I've never run into any project using it in the wild.
sieabahlpark 10 hours ago [-]
[dead]
rootnod3 1 days ago [-]
ANSI CL is such a breath of fresh air nowadays. Does what you need, doesn't get in your way, comes with batteries included. And conditions are just god-tier.
shakow 9 hours ago [-]
> comes with batteries included
I like CL, but I can't agree that a stdlib that doesn't even have a string split function is batteries-included.
nobleach 4 hours ago [-]
And to bring it full-circle, this is the exact same thing I run into with Go. When I mention how nice it is that Lang X has feature Y, someone is quick to point out that either, "You can BUILD that in Go" or, "You don't really need feature Y". We've proven that we don't really NEED compilers either... but I would hate to have to do my job without them.
iosjunkie 1 days ago [-]
"properly designed" - ah yes, programming languages are famous for universally agreed upon design philosophies.
boxed 1 days ago [-]
I liked Objective-C (except the C parts). Such a breath of fresh air coming from C++ which was grown like a cancer with tons of features and you felt trapped by every one of them.
Objective-C in contrast was a very few additions thoughtfully added that composed cleanly and freed the programmer to actually get things done.
skywhopper 1 days ago [-]
You may be tired of languages evolving over time, but there is no other way to build a rich and useful language.
fhn 1 days ago [-]
so make your own and let's see how you do
chlorion 22 hours ago [-]
I am actually working on my own language, and getting something better than Go is actually not that difficult!
The hard part about making a language is creating the stdlib and tooling and support for the language, but actually creating a language itself that has more features and better features than go can be done by a single person in a few months or a year probably, depending on how much experience they have.
Generics specifically are a great example here. A single person can implement a language with go-level generics fairly easily.
ratrace 13 hours ago [-]
[dead]
rootnod3 1 days ago [-]
Have you?
maccard 1 days ago [-]
There’s a fine line between being willing to change your mind and getting the basics wrong. Go has repeatedly gotten the basics wrong.
whoiskevin 1 days ago [-]
Declaring a highly successful language as having the basics wrong means that you are not correct about the basics that were needed.
maccard 1 days ago [-]
Something can be highly succesful in spite of having glaring design flaws. Nobody is claiming go isn't wildly succesful, but it's _in spite_ of these issues. It was clear over a decade ago that iota, gopath, and lack of generics were massive kneecaps to the language; go changing it's mind on those things isn't progress it's just getting the fundamentals wrong.
A good example of where they're kind of stuck is date formatting - it's stupid, unclear, and likely a mistake, but it's not a fundamental flaw; it's just a quirk.
9rx 1 days ago [-]
Why is iota a massive kneecap to the language? It is semantically identical to enum in C and Typescript.
The trouble is that Rust is older than Go and it was already confusing people into thinking enums and sum types are the same thing, so by using slightly different syntax, iota, Go avoided the whole confusion of users thinking that enums would behave like sum types instead of actual enums.
Is your attempt at making a point that not having sum types is the massive flaw? Sum types are a useful construct, to be sure, but there are plenty of good languages without them. That's more on the design quirk end, realistically.
maccard 1 days ago [-]
> Why is iota a massive kneecap to the language? It is semantically identical to enum in C and Typescript.
iota is a massive kneecap _because_ it's semantically identical to enum in C and Typescript.
> Is your argument actually that not having sum types is the massive flaw? Sum types are a useful construct, to be sure, but there are plenty of good languages without them. That's more on the design quirk end, realistically.
In a dream world sure we'd have full blown sum types (and that would give a result type which would also solve a lot of the nil-interface-combined-with-error-handling issues that I've ran into when working with go), but I can forgive that. The problem is this - https://www.zarl.dev/posts/enums
The only case I see made in there is that it doesn't like how Go implicitly converts consts. While that may be a reasonable criticism, it doesn't have anything to do with iota. It is related to the type system and applies in general. Consider the same problem exhibited here:
type Email string
func Send(email Email)
func() { Send("invalid") } // Converted string const does not satisfy Email type expectations
Perhaps you accidentally offered the wrong link?
It was made abundantly clear when Go was released that it was intended to "feel like a dynamically-typed language". Being able to pass arbitrary values is perfectly in line with a dynamically-typed language. Realistically, the type system in Go is there to give the compiler optimization hints, not to offer type safety. Go was targeted at those wanting to use Python, without the programs being painfully slow to run. How much of a kneecap is implicit type conversion, really, when it is already in line with what the target audience is accustomed to? It is a quirk at best.
maccard 23 hours ago [-]
> It was made abundantly clear when Go was released that it was intended to "feel like a dynamically-typed language".
If I google this quote a comment from you comes up here on this exact topic, where you seem to have completely missed the point there too. If I link to the docs [0], the full quote is "It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language. " So it is a statically typed language first and foremost. If you want to rehash the discussion and tell people that a flawed type system that people have been asking for a solution to for close to a decade [1] you can just re-read the last time the arugments were made as I don't think I'm going to make any headway there.
> So it is a statically typed language first and foremost.
Right, because that is primarily how it makes things fast. Python is slow largely because it spends an inordinate amount of time trying to figure out what things are. Go knows what things are at compile time because the static type system tells it what things are and thus doesn't have to waste runtime compute on figuring out what things are, aside from when you use the reflect package, like Python does. That was its value-add — that it is kind of like Python, but faster. We already went over this...
> If I link to the docs [0]
I said original announcement, so I'm not sure why you wouldn't look there? Trying to be obtuse on purpose? Regardless, performance was indeed considered more important than being dynamically-typed. After all, if performance wasn't a concern then you'd just use Python. Go exists only because it was solving a problem that wasn't already solved. Slow Python was already solved. Type safety was already solved. It didn't need to go into those territories.
stouset 23 hours ago [-]
> Why is iota a massive kneecap to the language? It is semantically identical to enum in C and Typescript.
So is nil. Care to make the same argument?
9rx 22 hours ago [-]
Typescript has something resembling nil as inherited from Javascript, so presumably that's what you are referring to. C and Javascript are the most widely used languages in existence. Clearly they weren't kneecapped. It mightn't be to your preference, but "I don't like it" would be an atypical use for the word "kneecap".
There is a stronger case to be made for the other two. Calling GOPATH a design mistake is a stretch as it was perfectly suited to use within Google, but it didn't fit the typical solo developer's environment. Lack of generics made writing certain types of code difficult. You could be convincing in suggesting that Go did end up being used less than it otherwise would have because of those choices.
iota? It's just a construct that generates numbers (an enum). How does that kneecap anything? If it really bothers you, you can manually number the values by hand instead. Why would anyone reject a language because it allows you to optionally choose to have the compiler assign numbers automatically instead of forcing you to do it manually? The answer is nobody. In fact, most popular languages have something equivalent to iota.
stouset 22 hours ago [-]
Nil exists in C and is widely considered to be a massive mistake in the language. An understandable one at the time, but a mistake nonetheless.
Thus “go borrowed it from C, therefore it can’t have been a mistake” is a pretty lame take. The whole point of a new language is to make improvements on what’s out there already. Go missed an opportunity to fix one of C’s most notorious mistakes. So yes, they kneecapped themselves by forcing all of the users of Go to continue dealing with this well-known footgun.
Does it mean Go isn’t popular? Of course not. C was popular. PHP was popular. JavaScript is popular. Go is popular. This is always in spite of their faults. But Go could have been better.
reilyroadster 6 hours ago [-]
Nil is not the problem, practically every language has such a concept - it's pretty much required to make things like Maybe/Optional work.
The problem is the implicitness of it and how easy it is to produce them and forget to check for them.
9rx 22 hours ago [-]
> Nil exists in C and is widely considered to be a massive mistake in the language.
You're almost there, but it is wildly considered to be massive mistake in context of arrays. C has weird array semantics that are confusing and hard to get right, even for seasoned developers. That is where NULL comes to bite people time and time again. Go did not inherit C's arrays. Neither did Javascript. They go out of their way to avoid what C did. In Go, you can come close to the same semantics if you use the unsafe package, but take note the name.
Yes, they still have nil, but the scope is tightly constrained and while it is technically possible to misuse, you have to try pretty hard to do so. There are many other things that are more likely to end up being misused. Those would be the more massive mistakes.
> But Go could have been better.
Obviously. Every language ever created can be better.
stouset 22 hours ago [-]
> You're almost there, but it is wildly considered to be massive mistake in context of arrays.
From Tony Hoare: "I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object-oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years."
Odd that he wouldn't mention the word "array" anywhere in that quote.
> Yes, they still have nil, but the scope is tightly constrained and while it is technically possible to misuse, you have to try pretty hard to do so.
Like by not remembering to check if a pointer is nil? Or actually comparing one typed nil to a different typed nil?
> Obviously. Every language ever created can be better.
Only one of us is arguing that reimplementing C’s mistakes is actually a good thing.
9rx 22 hours ago [-]
> Like by not remembering to check if a pointer is nil?
Rule of thumb is to not introduce values that aren't valid. Equally, don't put in -1 for an age value, even if the language allows you to. You might later forget to validate that the age value is valid.
Yes, mistakes are possible, but these types of mistakes are pretty rare. There is some value in eliminating the possibility of those mistakes, sure, but we cannot pretend that it comes cost-free. There is good reason why almost nobody uses Rocq.
> Or actually comparing one typed nil to a different typed nil?
This is something that is likely to confuse, but not a facet of nil. It is related to interfaces. Let's not flail around like one of those wacky blow up things at the used auto lot.
stouset 20 hours ago [-]
Ah the good old “just be a perfect programmer and you won’t run into any issues” argument. Haven’t heard that one in awhile.
9rx 19 hours ago [-]
If only you had made it to the second paragraph.
Hey, at least we can now understand why you have such a hard time with nil, so that's something.
shakow 8 hours ago [-]
Your second paragraph doesn't hold after one minute spent reading the CVEs list.
jolux 1 days ago [-]
Rust is technically older than Go, but who was actually using it when Go 1.0 came out in 2012? Rust 1.0 wasn’t until 2015.
9rx 1 days ago [-]
The social landscape doesn't depend on anyone actually using it. However, 1.0 isn't a significant milestone like you suggest either. For a current example, Zig is relatively popular today despite not yet reaching 1.0.
stouset 22 hours ago [-]
Do you just forget the things you write in earlier comments?
> Rust is older than Go and it was already confusing people into thinking enums and sum types are the same thing
Of course the social landscape depends on people actually using it. None of the people who weren’t using Rust at the time were magically confused about enums and sum types by the mere existence of some new and experimental language.
Rust barely existed at the time Go was first being developed. And given the history of Go and the notoriety of its core team for flatly ignoring prior work in programming languages, it’s extremely unlikely that Pike et al gave more than a cursory glance to what nascent Rust was doing at the time.
But even if they had, to suggest that they intentionally replicated a dumb thing from C but gave it a different name to avoid users being confused by a different thing from a language that roughly nobody knew about at the time is bananas.
9rx 21 hours ago [-]
> Of course the social landscape depends on people actually using it.
That's nonsense. Brainfuck has shaped the social landscape despite effectively nobody using it, and absolutely nobody using it for any real work. The social landscape is not at all dependent on use.
> And given the history of Go and the notoriety of its core team for flatly ignoring prior work in programming languages
Huh? Go comes straight out of prior work. It is nearly indistinguishable from Alef. What the Go language flatly ignored was being innovative. Reasonably so, of course. It wasn't trying to innovate in programming languages so that we'd have another to throw on the heap of languages nobody uses. It was trying to solve a specific business problem using well-established methods.
ratrace 13 hours ago [-]
[dead]
jeswin 1 days ago [-]
It's a highly successful language because (1) it was backed by Google, and (2) created by Robert Griesemer, Rob Pike, and Ken Thompson.
If it came out of anywhere else, it might have struggled even to hit the homepage here.
amazingamazing 1 days ago [-]
This logic is easily shown to not hold. Why isn't Carbon, Dart, etc. not really popular then?
doodpants 1 days ago [-]
I can't speak about Dart, but Carbon had just barely started development when it was first announced 4 years ago, and is currently presented as an experimental language that is not yet ready for use [0].
Dart was relegated to effectively being Flutter. It was originally intended to supersede JavaScript but no other engine would commit to that. It failed at its initial goal and hasn’t really found a footing outside of Flutter.
Carbon is by its own admittance not ready to use and I think mostly relegated to solving Google’s problems with C++ right now.
Both of them didn’t ship with a standard library as robust as Go’s.
One thing that made Go popular out of the gate is it is extremely good fast to build out robust HTTP services and infrastructure.
This is a very common use case and they tailored Go to be a great fit for it. You can build your entire backend without a single third party module if desired using Go’s standard library and it isn’t terribly complicated to do so.
voidfunc 1 days ago [-]
Its just bitter dorks bitter their pet language with cutting edge programming abstractions didnt make it to the big leagues.
n6242 1 days ago [-]
By that logic Windows would be the best operating system ever and perfect in every way, and anyone who disagrees must be wrong about how an OS should be.
hocuspocus 1 days ago [-]
And Javascript and Python the best languages.
someone_19 24 hours ago [-]
So you mean to say that PHP5 and Js from 2007 had a well-founded design?
pjmlp 10 hours ago [-]
Yes, all great examples of language design. PHP, Java, JavaScript, C, C++, Go
The basics of a programming language were wrong. The basics of marketing were very right. Those are not the same.
9rx 1 days ago [-]
An engineer, of course, understands that there is no such thing as "wrong", only different tradeoffs, but with the rise of "vibe coding" you don't need to be an engineer to play in the world of programming anymore.
OtomotO 1 days ago [-]
cough JavaScript cough
Jleagle 1 days ago [-]
Sounds like you want this feature, and you just got it. Not sure how that's wrong. You don't add in every feature from the start.
maccard 1 days ago [-]
I wanted it 10 years ago.
1 days ago [-]
tux3 1 days ago [-]
I don't think anyone admitted any wrong or had any big change in philosophy. It's always a good thing to learn something along the way. But the current message seems to be that this was the plan all along, and it just took some time to design properly.
Of course adding generics is not something that every language needs to do. Scripting languages like Ruby don't really need this style of generics. It doesn't fit the design of the language, and it's not even clear what that would look like in Ruby.
But static typing with generics does solve a recurring problem, and we've seen some real convergence towards type hints and type systems even in staunchly dynamic scripting languages. Modern Javascript is now mostly Typescript, and they've successfully retrofitted a very advanced type system in the last place I would have expected 20 years ago.
galangalalgol 1 days ago [-]
Type hinting seems like the worst of both. You pay the cost on refactor to go change them all, where dynamic typing or static type inference avoid that. You also don't have any of the benefits of static or dynamic typing. My strong preference is static typing with good inference and an ide that shows the inferred types everywhere when asked. Dynamic typing can make some tasks dramatically easier, I'm just not capable of using them without making hideous mistakes.
layer8 1 days ago [-]
It’s still annoying ~20 years after Java did the same mistake of not including generics, which was already clear to many people with C++ experience back then.
someone_19 24 hours ago [-]
...and Java didn't even have basic enums or sum types from the beginning. But it had null.
They added enums, they added sealed classes. They're trying to get rid of null (apparently it's really hard). The problem is that in 2012, when go 1.0 was released, this should have been obvious to everyone.
Here's a famous discussion from 2009, three years before the 1.0 release (tldr: facepalm)
That is an interesting read, seems some were having a hard time grasping the benefit of having compiler checks for potential null dereferences. Having worked with null safety in TypeScript and Kotlin the extra bit of strictness is nice.
layer8 23 hours ago [-]
I remember back in 1995 thinking that it was stupid for Java not to have generics, so instead you had to always cast Vector/Hashtable elements from Object, or implement your own type-specific container classes for every element type (and there wasn’t even a preprocessor to facilitate the latter).
Sum types I didn’t really miss, because you can implement a type-safe equivalent using the Visitor pattern, and retain an interface-implementation separation that native sum types typically don’t provide.
pjmlp 10 hours ago [-]
The biggest design issue with adding value types and non nullable to Java, is that the number one design requirement of any solution is not to break Maven Central.
Every compiled JAR out there has to keep working as always on a JVM with updated semantics, and worse code has to be compatible, when passing class instances around between old and new code.
Then there are the guest languages on the JVM as well.
ivanjermakov 18 hours ago [-]
Worst thing a programming language can do is introduce core semantics changes after 1.0. See Python 2 -> 3 and Zig's *gates.
metaltyphoon 17 hours ago [-]
Except Zig is not 1.0
ivanjermakov 9 hours ago [-]
Of course, just a good case study of how radical languge changes are received by users.
Cthulhu_ 1 days ago [-]
Where did "they" say "we" didn't need generics? That sounds like a bad faith / misinterpretation / straw man; as someone else pointed out, they postponed generics until they figured out the use cases and whatnot.
Remember that the generics implementations in other languages (like Java) take up half the spec + implementation - that's not something that Go wanted.
tines 1 days ago [-]
From another commenter here:
> The post quotes the Go FAQ as saying, "we do not anticipate that Go will ever add generic methods".
entrope 1 days ago [-]
You keep posting that. Do you understand the difference between that and "we anticipate that Go will never add generic methods"? What they actually said shows epistemic humility and recognizes that they might change their mind.
tines 1 days ago [-]
Hi! I'm a human being that is trying to understand the world and make friends along the way. I see you are too! Pleased to meet you.
You asked the question
> Where did "they" say "we" didn't need generics?
And I (re)posted a quote from them, which sounds to me like, at the time, they believed that "we" Go users didn't need generics.
They may have changed their mind, which is totally fine! But I do think it sounds like the person you were replying to wasn't commenting in bad faith or misunderstanding or fighting a straw man as you posted. Seems like a reasonable interpretation of what the Go devs had said at one point. To each his own though!
ncruces 22 hours ago [-]
Do you understand the difference between generics and generic methods?
We already had generics when they wrote "we don't anticipate adding generic methods."
tines 22 hours ago [-]
The OP didn't mention generics or generic methods :) They referred to something that was added, after the Go team didn't anticipate adding it. Sounds like a fair point to me!
MrBuddyCasino 10 hours ago [-]
What did they say about proper enums.
someone_19 9 hours ago [-]
You already have iota. Type safety is not needed by design:
> Go intentionally has a weak type system... Go in general encourages programming by writing code rather than programming by writing types...
Of course, if you go back and watch the original Go announcement it said that it would need generics once they figured out how to do it. And when the first version of generics landed it was said that generic methods would be added later, once they figured out how to do it. So that isn't applicable here. The need was always recognized.
zarzavat 14 hours ago [-]
If Go had just taken an off-the-shelf implementation of generics in 2009 then they could have spent the last 16 years deliberating over something useful, rather than attempting to reinvent programming language theory.
The impression I have always gotten from Go's designers is that they are rather arrogant and averse to the idea of using other people's work. They want to develop everything from first principles, but by so doing end up with poor reinventions of well-studied concepts.
9rx 13 hours ago [-]
> and averse to the idea of using other people's work.
They did use someone else's work, though. If you recall, Philip Wadler (of Haskell fame) designed Go's generics.
> but by so doing end up with poor reinventions of well-studied concepts.
Which is funny as there is probably nobody on earth that would be more capable than Wadler to get the job done. His pedigree in that area of work is pretty astounding. If he couldn't do more than create a poor reinvention, what hope did the laymen working on the Go core team have?
Answer: They had no hope. It's not like they weren't trying. Ian Lance Taylor, for instance, is well known for beginning work on generics in Go before it was even first released to the public. He, among others, quite simply, were unable to figure it out.
Everything looks easy and straightforward when observed comfortably from an armchair, I suppose.
pjmlp 10 hours ago [-]
Yes, the same guy that supported the effort to add generics to Java, a decade beforee Go came to be, talk about not getting language design history.
Stop excusing them, they were the first to acknolowdge being wrong in first place,
"They are likely the two most difficult parts of any design for parametric polymorphism. In retrospect, we were biased too much by experience with C++ without concepts and Java generics. We would have been well-served to spend more time with CLU and C++ concepts earlier."
What is there to excuse? Your quote confirms that they simply don't know what they're doing as was already established. Not that anyone should expect them to. They're just regular average humans, same as every other random Joe you encounter while walking down the street, who all equally have their own failings and shortcomings. Why HN is constantly trying to put these particular people on a pedestal, I'll never know. Jealously that regular bumbling idiots just like them accidentally stumbled into creating something popular (for some definition of popular), perhaps?
38 minutes ago [-]
fhn 1 days ago [-]
complaining about things given to you for free
doodpants 1 days ago [-]
I frankly don't buy into this trope that a lack of monetary cost should shield something from criticism. Anything created by humans for other humans, especially tools meant for getting work done, should certainly be open to evaluation/judgement/critcism, regardless of whether the creator chooses to charge for it.
And it's not like Golang is some freshman student's hobby project; it was created by one of the world's largest tech companies, by people with a strong pedigree in programming language design.
msaher66 9 hours ago [-]
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.
tapirl 8 hours ago [-]
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...
msaher66 4 hours ago [-]
Thanks for sharing. I had no idea Go's generics had this many limitations.
apatheticonion 5 hours ago [-]
I wrote Go professionally for years. I don't know how many hours I've spent debugging and reviewing nil pointer bugs and race conditions. Generics were sorely needed but the initial implementation was lacking.
I moved to Rust professionally 4 years ago and haven't looked back. Mutex<T> Option<T> Result<T, Err> are all phenomenal.
I've written everything from web backends, frontends (hurry up wasm, seriously), to Node.js and Python extensions.
Web backends use under 1mb of memory and can support hundreds of thousands of concurrent users on a $2/m VPS. Frontends can be beautifully multithreaded. Native extensions can dance between OS threads and multi-threaded runtimes.
When I review code I focus only on the logic, not sidetracked by reasoning about race conditions or anything. Great when you review the work of less experienced contributors.
The ultra strict compiler is extremely helpful with LLMs. You bounce back and forth until it compiles and, if it compiles, it's usually correct.
It's at the point where I can't really see a use case for another language - and yet, no one uses it! It's madness!
davidatbu 2 hours ago [-]
By frontend, do you mean wasm in the browser? I'd have expected multithreading there, especially in wasm, to be extremely unergonomic.
Maybe you mean to refer to concurrency?
root-parent 5 hours ago [-]
I have nothing to add to your comment, but it matches so eerily my exact experience, that I just wonder if your alias is a second account of mine :-)
kardianos 1 days ago [-]
This is great. Will be useful for data access methods!
As for the detractors, from the first generics proposal this was called out as a "not now", not never. There were questions of implementation. They aren't a super large team, and they try to do things incrementally and do them well.
tczMUFlmoNk 1 days ago [-]
> As for the detractors, from the first generics proposal this was called out as a "not now", not never.
What? The post quotes the Go FAQ as saying, "we do not anticipate that Go will ever add generic methods". There is also some similar discussion of the original generics proposal, with language like "then it's much less clear why we need methods at all". (I'm omitting some context, but I don't feel that it changes the meaning.) Those feel much closer to "never" than "not now".)
The post is also subtitled "A change of view".
stouset 1 days ago [-]
No, you’re clearly wrong; golang was always going to add support for generic functions.
Everyone also wanted and accepted the need for generics. It was always something they wanted to add to the language. Rob Pike never said that that kind of abstraction isn’t what golang is for. It was always just a matter of getting the design right.
Go has always been a systems language. It was one when we thought it was going to fit nicely for low-level, high performance use-cases. Given that the GC, runtime overhead, lack of control over memory layout, and other issues made it a poor fit for what we historically thought were systems language tasks, it’s still a systems language because we’ve grown to understand that the term “systems program” has always meant network middleware that shuttles around JSON and transforms it.
Dependency management too. Modules were something that nobody argued were unnecessary. None of the language developers ever claimed that “you should always build against HEAD, and if upstream breaks you, that’s a coordination problem to be solved socially”. The community didn’t need to independently invent godep, then glide, then govendor, then dep, before the core team finally shipped modules. That was just enthusiastic parallel exploration of a problem space that everyone agreed was a problem.
GOPATH was always understood to be an awkward temporary scaffold that everyone tolerated while the real solution was being designed. The single-workspace model was never defended as philosophically correct or a deliberate feature of the language. When modules arrived, everyone was simply relieved that this obvious stopgap was finally replaced.
The core team always intended to add builtins for min/max. Nobody ever told you to just write `if a > b { return a }; return b` yourself because it was “only two lines.” The fact that every Go codebase in existence had its own copy of this logic, typically buried in a file called util.go, was not evidence of anything being missing from the language.
Range was always a stopgap before iterators could be implemented. Nobody ever argued that iterators were needlessly complicated and went against the spirit of the language. The slices and maps packages provided important missing features that everyone using the language wanted.
Everyone agrees that errors were anemic from the outset. errors.Is/errors.As are nice additions but everything was Just Fine™ before they were added.
Speaking of errors, having two lines of error-handling boilerplate for every line of code is good, and right, and perfect. It’s not verbose; it’s “explicit”. But when that gets changed to be less verbose, we will all agree that it was always a pain and made reading code unnecessarily more difficult and that everyone always expected this to be fixed some day.
I personally can’t wait to see what next development will never have been “against the Go philosophy” and definitely not something that gophers argued was perfect the way it was any time misguided malcontents and rabble-rousers wrongly tried to suggest the language wasn’t perfect the way it was.
bborud 6 hours ago [-]
You have those who obsess over feature lists for languages and then you have those who obsess over friction, productivity, ease of use and developer ergonomics.
If I were uncharitable I might call the categories "people who are somewhat removed from reality" and "people who inhabit observable reality". Avoid the former, treasure the latter.
Languages evolve for a reason and nobody should give a shit about people who do not understand why.
galkk 8 hours ago [-]
I love your rant.
The writing is on the wall for next development.
“For the foreseeable future, the Go team will stop pursuing syntactic language changes for error handling. We will also close all open and incoming proposals that concern themselves primarily with the syntax of error handling, without further investigation.”
cyphar 23 hours ago [-]
> The community didn’t need to independently invent godep, then glide, then govendor, then dep, before the core team finally shipped modules. That was just enthusiastic parallel exploration of a problem space that everyone agreed was a problem. When modules arrived, everyone was simply relieved that this obvious stopgap was finally replaced.
And of course, it was replaced with a more correct implementation that was incompatible with that awful stopgap because semantic correctness trumps all. vendor/ trees and GOPATH were never meant to be remotely compatible, and don't you know -- the Go compatibility guarantee(TM) doesn't apply to misuse of GOPATH to work around shortcomings^Wwell-considered designs of Go, even if it breaks the largest Go project at the time!
(/s It still shocks me that they decided to drop "src" from vendor/src and break compatibility when they finally got around to supporting vendoring despite every tool using it. And symlinks don't work because Plan 9 is the future!!)
jpc0 24 hours ago [-]
> term “systems program” has always meant network middleware that shuttles around JSON and transforms it.
Who are we that has always defined that term that way. For any systems programmer golang has pretty much not been a solution.
Systems is below layer 4 of the network stack, it is building the network stack in the first place.
bborud 5 hours ago [-]
"Systems program" or "systems programming" is a meaningless term because people use it differently. Discussing what it means is a waste of time. It is better to be specific.
masklinn 24 hours ago [-]
I think your sarcasm detector has gone missing chief.
Joker_vD 21 hours ago [-]
Um, there is user-level implementation of TCP/IPv4+IPv6 stack in Go [0], developed and used by Google as part of its gVisor.
> I personally can’t wait to see what next development will never have been “against the Go philosophy” and definitely not something that gophers argued was perfect the way it was any time misguided malcontents and rabble-rousers wrongly tried to suggest the language wasn’t perfect the way it was.
I really hope it is more ergonomic error handling. Or maybe sum types/discriminated unions.
stouset 23 hours ago [-]
Those are fine the way they are. I don’t want the language getting more complicated. Rob Pike has said they’ll never happen. Suggestions like this are extremely against the Go way of doing things. Actually the verbosity is a good thing. It’s explicit.
Joker_vD 22 hours ago [-]
> I really hope it is more ergonomic error handling.
They've publicly said no more language changes specifically for better error handling are coming, it will at most be the library-level improvements.
stouset 22 hours ago [-]
So this would be in line, then, with all of the other features they’ve publicly said aren’t coming and aren’t necessary but then do anyway after retconning their original stance?
Joker_vD 21 hours ago [-]
Could esteemed commenters please read the words the Go language team actually wrote?
> Or, we could decide that parameterized methods do not, in fact, implement
interfaces, but then it's much less clear why we need methods at all. If
we disregard interfaces, any parameterized method can be implemented as a
parameterized function.
> So while parameterized methods seem clearly useful at first glance, we
would have to decide what they mean and how to implement that.
Well, they have finally decided what parameterized methods actually mean, and they see how to implement that, and it all seems clearly useful. So...
someone_19 9 hours ago [-]
> No, you’re clearly wrong; golang was always going to add support for generic functions.
Let's get this straight. I'll give you a long quote from Rob Pike's article where he describes the history of the go language:
"""
One thing that is conspicuously absent is of course a type hierarchy. Allow me to be rude about that for a minute.
Early in the rollout of Go I was told by someone that he could not imagine working in a language without generic types. As I have reported elsewhere, I found that an odd remark.
To be fair he was probably saying in his own way that he really liked what the STL does for him in C++. For the purpose of argument, though, let's take his claim at face value.
What it says is that he finds writing containers like lists of ints and maps of strings an unbearable burden. I find that an odd claim. I spend very little of my programming time struggling with those issues, even in languages without generic types.
But more important, what it says is that types are the way to lift that burden. Types. Not polymorphic functions or language primitives or helpers of other kinds, but types.
That's the detail that sticks with me.
Programmers who come to Go from C++ and Java miss the idea of programming with types, particularly inheritance and subclassing and all that. Perhaps I'm a philistine about types but I've never found that model particularly expressive.
My late friend Alain Fournier once told me that he considered the lowest form of academic work to be taxonomy. And you know what? Type hierarchies are just taxonomy. You need to decide what piece goes in what box, every type's parent, whether A inherits from B or B from A. Is a sortable array an array that sorts or a sorter represented by an array? If you believe that types address all design issues you must make that decision.
I believe that's a preposterous way to think about programming. What matters isn't the ancestor relations between things but what they can do for you.
That, of course, is where interfaces come into Go. But they're part of a bigger picture, the true Go philosophy.
"""
Rob Pike, 2012
I can draw a few conclusions from this: firstly, he didn't want to add generics at all because he didn't think they were useful, and secondly, he doesn't understand programming very well and doesn't know what generics are and confuses them with inheritance.
10 hours ago [-]
kardianos 1 days ago [-]
I could be mis-remembering it. I didn't look up and src it.
dude250711 1 days ago [-]
Gophers are usually quite fast, perhaps an elderly turtle would be a better mascot?
rob74 1 days ago [-]
In day-to-day usage, the (fast) compilation speed matters much more than the (slow) implementation of new features.
christophilus 1 days ago [-]
I totally agree, but I'd go further and argue that slow implementation of new features is itself a desirable trait. It's one of the reasons why why I like both Go and Clojure.
aktau 1 days ago [-]
Spot on. Heaven forbid it turns into a C++ (I'm not a Rust practitioner but from the outside it seems to accrete features pretty quickly as well).
The ease of grokking Go (both reading and writing) are big advantages, and facilitated by the "small" feature set of Go.
cookiengineer 1 days ago [-]
> 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
kibwen 4 hours ago [-]
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.
reactordev 1 days ago [-]
This resolves a big gap in generics for most people coming from other languages to go so I completely approve this direction. Not saying use it everywhere but if you must use it, it’s better to have it on the struct than call a module level generic func.
ethin 10 hours ago [-]
Watch as this gets rejected for nonsensical reasons just like the issue about adding uint128, which also has been open for an extremely long time and they keep moving the goalposts as to reasons why it can't be done...
piekvorst 10 hours ago [-]
It’s accepted.
ethin 10 hours ago [-]
Now there's a surprise. I've generally been very disillusioned with Go after they absolutely stonewalled everybody on uint128 (and continue to do so) for absolutely no reason (and ignoring that it would make many things in the language easier to express).
MrBuddyCasino 9 hours ago [-]
> it would make many things in the language easier to express
Like what?
ethin 49 minutes ago [-]
Like IPv6 addresses, UUIDs, the list goes on. Does it mean anything that Go themselves had to invent a custom uint128 type in the standard library because they didn't want to add it to the language? There's a very long list of instances of them stonewalling it here: https://github.com/golang/go/issues/9455
mackross 1 days ago [-]
What a happy surprise today! The amount of times I’ve had to do weird janky package APIs so the API was still reasonable is more than I can count.
samber 1 days ago [-]
OMG. I'm going to recode some of my libraries.
binary132 1 days ago [-]
Chasing a perceived gap between language features and user expectations has been and continues to be the greatest error in the leadership of Go.
pizzafeelsright 1 days ago [-]
The nagging imperative requires a stronger response than the capitulation of identity.
MrBuddyCasino 8 hours ago [-]
They should have blocked generics forever and instead have a proper enum. Unpopular opinion, I know.
throwpikerob 1 days ago [-]
[flagged]
sdevonoes 18 hours ago [-]
It certainly makes readability of Go code harder.
EdwardDiego 16 hours ago [-]
If generics replace code generation or hand coding a concrete implementation for specific types or... operating against interface{} and then switching based on runtime type, then I strongly disagree that it makes reading Go code harder, it makes it, IMO, easier.
It only died if you actually applied it in your own codebase - as with any feature, using it is optional.
9rx 16 hours ago [-]
Code is written to be read, and typically for other people to read. So, in practice, it is only optional if everyone else agrees to not use it.
p0w3n3d 11 hours ago [-]
what's wrong with `#define`? <laughing hysterically>
nah I'm kidding
<after 55 seconds>
Seriously, what's wrong with `#define`?
axod 1 hours ago [-]
And so the cycle will continue. Always a shame when languages cave like this and add extra unnecessary complexity and error prone hard to parse syntax.
It'll be interesting to see the next language that comes along rejecting bloat in favor of simplicity, and then we can all start again.
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 using runtime reflection is mostly that it's slow. But that runtime reflection is exactly how you would work around it today.
For the Identity example, could the interface be compiled to be basically equivalent to:
Identity(any) any
and then at the callsite add a cast of the return type to T?
I suppose primative non-pointer types add a bit of a wrinkle but even if it generic methods was restricted to pointer types, that's better than nothing. And the number of those types is relatively small, so when the implementation is compiled it could just instantiate method implementations for all the primative types, if they apply, and then maybe remove them if they aren't needed at link time.
Of course it's also possible there is some detail I've missed.
[1]: https://go.googlesource.com/proposal/+/refs/heads/master/des...
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 of complexity, with surprising failure modes if these two are not bug-compatible.
> But that runtime reflection is exactly how you would work around it today.
I think the overwhelming majority of people will "work around it" by just not trying to use generic methods.
My understanding is that go already has a hybrid system works at compiletime and sometimes at runtime.
My point is for interface generics it could just always use a single instantiation. Similar to what java does.
Or alternatively, go could go the other direction and add a new type of interface that is only for use in generic constraints, and then generic methods could be part of that interface, but not normal interfaces, so that the generic methods could be called from other generic functions. That would be similar to rust and c++.
Can't speak too deeply for Go specifically, but I do know on .NET one of the big reasons generic methods where T is a structure gets monomorphized per type, is so that stack size is adjusted and potentially even arg passing (i.e. large struct) as far as the caller/callee.
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:
and something like this in package b: and something like this in package c: And then do: 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:
And you do: 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:
Because the compiler knows statically from the Foo call sites which concrete methods it needs to generate.But, when you introduce a dynamic cast:
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.
It is Apple's school of design, think different, ah, actually, there are reasons why the fence is in the middle of nowhere.
Then the design ends up half way there versus being done properly from the beginning.
Yeah very critically.
Less "we know better", more "actual history".
This is nonsensical. Monads define a strict set of behaviors formalized as "monad laws"[0].
Perhaps what you want is a container which adheres to monad laws capable of abstracting exceptions. Two exemplars of same are Haskell's Data.Either[1] and Scala's Either[2].
0 - https://wiki.haskell.org/Monad_laws
1 - https://hackage-content.haskell.org/package/base-4.22.0.0/do...
2 - https://www.scala-lang.org/api/3.8.3/scala/util/Either.html
https://wiki.haskell.org/index.php?title=All_About_Monads
That is what I meant. Struggling to picture what the other "nonsensical" thing is.
No contributor to Go is responsible for "introducing monads to computer science", as the Monad concept is a member of (or defined by if you prefer) Category Theory[0].
0 - https://en.wikipedia.org/wiki/Category_theory
Still, in this case, half the feature is better than none at all, IMO.
[1]: https://doc.rust-lang.org/reference/items/traits.html#r-item...
The one additional piece of information you need is that in Go, all interfaces are supposed to be trait objects. The exception are union-elements, but that's really a restriction the Go team is trying to remove, not a model to base more features on.
This aspect is what prevents you from statically knowing which interface-implementations you need to generate for a specific concrete type. There could always be new ones added at runtime.
Structural typing is the term typically used to describe "static duck typing".
But who is to say that dynamic evaluation isn't the special case?
Did you?
Dynamically typed/untyped languages finding that strict and visible typing is actually good is another
Debatable how much they have been "right", although this gets them somewhat closer. And I think they have not been "wrong" in the ways they wanted to avoid (they referenced some issues with Java generics as prior art, although I forget the details).
> The post quotes the Go FAQ as saying, "we do not anticipate that Go will ever add generic methods".
https://www.bible.com/bible/compare/MAT.21.28-31
> They didn't say they never wanted to do generics, but that they did want to take their time and do them right.
Then when the language is inevitably changed for the better, resolving the complaint, suddenly it was always going to happen and it was just a matter of getting the details right.
Every other language community I can think of is more than willing to acknowledge the shortcomings of their language. “Yeah, this kind of sucks in principle but it’s not something that gets in the way in practice” is a fine perspective. So is “this was a tradeoff; we went in this direction and these are the resulting downsides”. But the golang community practically trips over themselves to constantly argue that obvious shortcomings in the language are actually a good thing and we just don’t get it.
Nobody is saying the language shouldn’t improve. We’ve all been begging the language to improve. But we’re also tired of the constant, obvious, and shameless gaslighting from the community whenever things do get better. You aren’t going to like the comparison, but it’s extremely Trumpian.
He wasn't the only one but he certainly took it to the extreme.
[1]: https://research.swtch.com/dogma
> But we need help from everyone. Remember that none of the decisions in Go are infallible; they’re just our best attempts at the time we made them, not wisdom received on stone tablets.
¹ I would argue that it is really, really hard to add generics to a language after it has already matured, and still "do it right" than to add it in the beginning. At least if you care about backwards compatibility. Backwards compatibility adds a lot of constraints to your generics system that will almost certainly lead to a sub-optimal design. And you will be stuck with a standard library, and a lot of existing ecosystem code that would benefit from generics, but don't because generics didn't exist when they were written. This is a lesson I wish go had learned from Java's generics.
I respect that.
My best litmus test these days is support for multidimensional arrays because it's always needed at some point in general purpose languages. CL and Ada had it right from the start while C++ needed C++23/26 to get std::mdspan and we still need to wrap it to pass the underlying/owned memory pool around (https://rosettacode.org/wiki/Multi-dimensional_array for more).
If I want a 1000x1000 array, representing it physically as a single 1000000-element array requires one allocation, and processing it element-by-element (assuming it's stored in the same order we're iterating over it) is sequential in memory and therefore very efficient.
Representing it as 1000 separate 1000-element arrays requires 1000 allocations, and pointer-chasing every time we move from one row to the next.
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 the original array.
Even in c++ could you not just define some int [1000][1000]foo? I've never really used C++ but my C knowledge assumption is that is 1000000 continuous elements.
Edit:
For the stack at least. On the heap, you'd need to use a single std::vector<int> and do the indices manually, or use mdspan
It works fine in C though, or FORTRAN, or Ada, or ALGOL 60, ...
NVidia has pivoted to design CUDA hardware with focus on C++ back in , and seems to be doing quite well for them.
CppCon 2017: "Designing (New) C++ Hardware”
https://www.youtube.com/watch?v=86seb-iZCnI
They were also the ones sponsoring the ISO work on mdspan, while HPC research labs are pushing for linalg on top.
I would rather be using Ada today, but that isn't how the world moves.
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.
I would very much prefer scheme if the different implementations had a working standard. But I can't take my Chez-scheme code and throw it into Guile-scheme.
But pretty good chance I can take my ECL code and throw it into SBCL or LispWorks.
Bah, I think this debate was already old when I first saw people arguing it on comp.lang.lisp in the 90s. I don't have a dog in this fight other than to reject the notion that Common Lisp is "coherent" and not "organically grown".
The original Scheme belongs in the category of languages like Standard ML and SmallTalk, where a small, careful, and talented group designed them with focus. Common Lisp seems like a bunch of smart people with competing interest and legacy baselines tried to meet in the middle. To the extent CL is more pragmatic, it's another example of "Worse is Better".
When I started building a Lisp-based machine learning framework, Guile seemed like the right choice because it provides GOOPS and generic functions, yet I still ended up with a lot of boilerplate to compensate for the lack of a strong type system.
Scheme feels to me like C is to C++: not ergonomic for large-scale application development. Go is one of those languages that has both minimalism and productivity.
Hopefully next they can add some error handling syntax and controls.
-- Greenspun's tenth rule
He had some lack of conviction to scope it so narrowly.
https://elixir-lang.org/blog/2023/06/22/type-system-updates-...
As an aside - D, Zig, Rust, even typescript got most of the lessons learned from C right
Zig has the (in)famous "Writergate": https://github.com/ziglang/zig/pull/24329
And besides Rust's high count of RFCs, there are things like async (I'm not complaining about it, but its an obvious large-scale "change"), module system changes, etc.
(To be clear, I like both languages a lot. But I wouldn't call them slow moving or right from the start.)
I like CL, but I can't agree that a stdlib that doesn't even have a string split function is batteries-included.
Objective-C in contrast was a very few additions thoughtfully added that composed cleanly and freed the programmer to actually get things done.
The hard part about making a language is creating the stdlib and tooling and support for the language, but actually creating a language itself that has more features and better features than go can be done by a single person in a few months or a year probably, depending on how much experience they have.
Generics specifically are a great example here. A single person can implement a language with go-level generics fairly easily.
A good example of where they're kind of stuck is date formatting - it's stupid, unclear, and likely a mistake, but it's not a fundamental flaw; it's just a quirk.
The trouble is that Rust is older than Go and it was already confusing people into thinking enums and sum types are the same thing, so by using slightly different syntax, iota, Go avoided the whole confusion of users thinking that enums would behave like sum types instead of actual enums.
Is your attempt at making a point that not having sum types is the massive flaw? Sum types are a useful construct, to be sure, but there are plenty of good languages without them. That's more on the design quirk end, realistically.
iota is a massive kneecap _because_ it's semantically identical to enum in C and Typescript.
> Is your argument actually that not having sum types is the massive flaw? Sum types are a useful construct, to be sure, but there are plenty of good languages without them. That's more on the design quirk end, realistically.
In a dream world sure we'd have full blown sum types (and that would give a result type which would also solve a lot of the nil-interface-combined-with-error-handling issues that I've ran into when working with go), but I can forgive that. The problem is this - https://www.zarl.dev/posts/enums
The only case I see made in there is that it doesn't like how Go implicitly converts consts. While that may be a reasonable criticism, it doesn't have anything to do with iota. It is related to the type system and applies in general. Consider the same problem exhibited here:
Perhaps you accidentally offered the wrong link?It was made abundantly clear when Go was released that it was intended to "feel like a dynamically-typed language". Being able to pass arbitrary values is perfectly in line with a dynamically-typed language. Realistically, the type system in Go is there to give the compiler optimization hints, not to offer type safety. Go was targeted at those wanting to use Python, without the programs being painfully slow to run. How much of a kneecap is implicit type conversion, really, when it is already in line with what the target audience is accustomed to? It is a quirk at best.
If I google this quote a comment from you comes up here on this exact topic, where you seem to have completely missed the point there too. If I link to the docs [0], the full quote is "It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language. " So it is a statically typed language first and foremost. If you want to rehash the discussion and tell people that a flawed type system that people have been asking for a solution to for close to a decade [1] you can just re-read the last time the arugments were made as I don't think I'm going to make any headway there.
[0] https://go.dev/doc/ [1] https://github.com/golang/go/issues/19814
Right, because that is primarily how it makes things fast. Python is slow largely because it spends an inordinate amount of time trying to figure out what things are. Go knows what things are at compile time because the static type system tells it what things are and thus doesn't have to waste runtime compute on figuring out what things are, aside from when you use the reflect package, like Python does. That was its value-add — that it is kind of like Python, but faster. We already went over this...
> If I link to the docs [0]
I said original announcement, so I'm not sure why you wouldn't look there? Trying to be obtuse on purpose? Regardless, performance was indeed considered more important than being dynamically-typed. After all, if performance wasn't a concern then you'd just use Python. Go exists only because it was solving a problem that wasn't already solved. Slow Python was already solved. Type safety was already solved. It didn't need to go into those territories.
So is nil. Care to make the same argument?
There is a stronger case to be made for the other two. Calling GOPATH a design mistake is a stretch as it was perfectly suited to use within Google, but it didn't fit the typical solo developer's environment. Lack of generics made writing certain types of code difficult. You could be convincing in suggesting that Go did end up being used less than it otherwise would have because of those choices.
iota? It's just a construct that generates numbers (an enum). How does that kneecap anything? If it really bothers you, you can manually number the values by hand instead. Why would anyone reject a language because it allows you to optionally choose to have the compiler assign numbers automatically instead of forcing you to do it manually? The answer is nobody. In fact, most popular languages have something equivalent to iota.
Thus “go borrowed it from C, therefore it can’t have been a mistake” is a pretty lame take. The whole point of a new language is to make improvements on what’s out there already. Go missed an opportunity to fix one of C’s most notorious mistakes. So yes, they kneecapped themselves by forcing all of the users of Go to continue dealing with this well-known footgun.
Does it mean Go isn’t popular? Of course not. C was popular. PHP was popular. JavaScript is popular. Go is popular. This is always in spite of their faults. But Go could have been better.
The problem is the implicitness of it and how easy it is to produce them and forget to check for them.
You're almost there, but it is wildly considered to be massive mistake in context of arrays. C has weird array semantics that are confusing and hard to get right, even for seasoned developers. That is where NULL comes to bite people time and time again. Go did not inherit C's arrays. Neither did Javascript. They go out of their way to avoid what C did. In Go, you can come close to the same semantics if you use the unsafe package, but take note the name.
Yes, they still have nil, but the scope is tightly constrained and while it is technically possible to misuse, you have to try pretty hard to do so. There are many other things that are more likely to end up being misused. Those would be the more massive mistakes.
> But Go could have been better.
Obviously. Every language ever created can be better.
From Tony Hoare: "I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object-oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years."
Odd that he wouldn't mention the word "array" anywhere in that quote.
> Yes, they still have nil, but the scope is tightly constrained and while it is technically possible to misuse, you have to try pretty hard to do so.
Like by not remembering to check if a pointer is nil? Or actually comparing one typed nil to a different typed nil?
> Obviously. Every language ever created can be better.
Only one of us is arguing that reimplementing C’s mistakes is actually a good thing.
Rule of thumb is to not introduce values that aren't valid. Equally, don't put in -1 for an age value, even if the language allows you to. You might later forget to validate that the age value is valid.
Yes, mistakes are possible, but these types of mistakes are pretty rare. There is some value in eliminating the possibility of those mistakes, sure, but we cannot pretend that it comes cost-free. There is good reason why almost nobody uses Rocq.
> Or actually comparing one typed nil to a different typed nil?
This is something that is likely to confuse, but not a facet of nil. It is related to interfaces. Let's not flail around like one of those wacky blow up things at the used auto lot.
Hey, at least we can now understand why you have such a hard time with nil, so that's something.
> Rust is older than Go and it was already confusing people into thinking enums and sum types are the same thing
Of course the social landscape depends on people actually using it. None of the people who weren’t using Rust at the time were magically confused about enums and sum types by the mere existence of some new and experimental language.
Rust barely existed at the time Go was first being developed. And given the history of Go and the notoriety of its core team for flatly ignoring prior work in programming languages, it’s extremely unlikely that Pike et al gave more than a cursory glance to what nascent Rust was doing at the time.
But even if they had, to suggest that they intentionally replicated a dumb thing from C but gave it a different name to avoid users being confused by a different thing from a language that roughly nobody knew about at the time is bananas.
That's nonsense. Brainfuck has shaped the social landscape despite effectively nobody using it, and absolutely nobody using it for any real work. The social landscape is not at all dependent on use.
> And given the history of Go and the notoriety of its core team for flatly ignoring prior work in programming languages
Huh? Go comes straight out of prior work. It is nearly indistinguishable from Alef. What the Go language flatly ignored was being innovative. Reasonably so, of course. It wasn't trying to innovate in programming languages so that we'd have another to throw on the heap of languages nobody uses. It was trying to solve a specific business problem using well-established methods.
If it came out of anywhere else, it might have struggled even to hit the homepage here.
0: https://github.com/carbon-language/carbon-lang#project-statu...
Carbon is by its own admittance not ready to use and I think mostly relegated to solving Google’s problems with C++ right now.
Both of them didn’t ship with a standard library as robust as Go’s.
One thing that made Go popular out of the gate is it is extremely good fast to build out robust HTTP services and infrastructure.
This is a very common use case and they tailored Go to be a great fit for it. You can build your entire backend without a single third party module if desired using Go’s standard library and it isn’t terribly complicated to do so.
Of course adding generics is not something that every language needs to do. Scripting languages like Ruby don't really need this style of generics. It doesn't fit the design of the language, and it's not even clear what that would look like in Ruby.
But static typing with generics does solve a recurring problem, and we've seen some real convergence towards type hints and type systems even in staunchly dynamic scripting languages. Modern Javascript is now mostly Typescript, and they've successfully retrofitted a very advanced type system in the last place I would have expected 20 years ago.
They added enums, they added sealed classes. They're trying to get rid of null (apparently it's really hard). The problem is that in 2012, when go 1.0 was released, this should have been obvious to everyone.
Here's a famous discussion from 2009, three years before the 1.0 release (tldr: facepalm)
https://groups.google.com/g/golang-nuts/c/rvGTZSFU8sY
Sum types I didn’t really miss, because you can implement a type-safe equivalent using the Visitor pattern, and retain an interface-implementation separation that native sum types typically don’t provide.
Every compiled JAR out there has to keep working as always on a JVM with updated semantics, and worse code has to be compatible, when passing class instances around between old and new code.
Then there are the guest languages on the JVM as well.
Remember that the generics implementations in other languages (like Java) take up half the spec + implementation - that's not something that Go wanted.
> The post quotes the Go FAQ as saying, "we do not anticipate that Go will ever add generic methods".
You asked the question
> Where did "they" say "we" didn't need generics?
And I (re)posted a quote from them, which sounds to me like, at the time, they believed that "we" Go users didn't need generics.
They may have changed their mind, which is totally fine! But I do think it sounds like the person you were replying to wasn't commenting in bad faith or misunderstanding or fighting a straw man as you posted. Seems like a reasonable interpretation of what the Go devs had said at one point. To each his own though!
We already had generics when they wrote "we don't anticipate adding generic methods."
> Go intentionally has a weak type system... Go in general encourages programming by writing code rather than programming by writing types...
https://github.com/golang/go/issues/29649#issuecomment-45482...
https://www.adacore.com/case-studies/nvidia-adoption-of-spar...
The impression I have always gotten from Go's designers is that they are rather arrogant and averse to the idea of using other people's work. They want to develop everything from first principles, but by so doing end up with poor reinventions of well-studied concepts.
They did use someone else's work, though. If you recall, Philip Wadler (of Haskell fame) designed Go's generics.
> but by so doing end up with poor reinventions of well-studied concepts.
Which is funny as there is probably nobody on earth that would be more capable than Wadler to get the job done. His pedigree in that area of work is pretty astounding. If he couldn't do more than create a poor reinvention, what hope did the laymen working on the Go core team have?
Answer: They had no hope. It's not like they weren't trying. Ian Lance Taylor, for instance, is well known for beginning work on generics in Go before it was even first released to the public. He, among others, quite simply, were unable to figure it out.
Everything looks easy and straightforward when observed comfortably from an armchair, I suppose.
Stop excusing them, they were the first to acknolowdge being wrong in first place,
"They are likely the two most difficult parts of any design for parametric polymorphism. In retrospect, we were biased too much by experience with C++ without concepts and Java generics. We would have been well-served to spend more time with CLU and C++ concepts earlier."
-- https://go.googlesource.com/proposal/+/master/design/go2draf...
What is there to excuse? Your quote confirms that they simply don't know what they're doing as was already established. Not that anyone should expect them to. They're just regular average humans, same as every other random Joe you encounter while walking down the street, who all equally have their own failings and shortcomings. Why HN is constantly trying to put these particular people on a pedestal, I'll never know. Jealously that regular bumbling idiots just like them accidentally stumbled into creating something popular (for some definition of popular), perhaps?
And it's not like Golang is some freshman student's hobby project; it was created by one of the world's largest tech companies, by people with a strong pedigree in programming language design.
I moved to Rust professionally 4 years ago and haven't looked back. Mutex<T> Option<T> Result<T, Err> are all phenomenal.
I've written everything from web backends, frontends (hurry up wasm, seriously), to Node.js and Python extensions.
Web backends use under 1mb of memory and can support hundreds of thousands of concurrent users on a $2/m VPS. Frontends can be beautifully multithreaded. Native extensions can dance between OS threads and multi-threaded runtimes.
When I review code I focus only on the logic, not sidetracked by reasoning about race conditions or anything. Great when you review the work of less experienced contributors.
The ultra strict compiler is extremely helpful with LLMs. You bounce back and forth until it compiles and, if it compiles, it's usually correct.
It's at the point where I can't really see a use case for another language - and yet, no one uses it! It's madness!
Maybe you mean to refer to concurrency?
As for the detractors, from the first generics proposal this was called out as a "not now", not never. There were questions of implementation. They aren't a super large team, and they try to do things incrementally and do them well.
What? The post quotes the Go FAQ as saying, "we do not anticipate that Go will ever add generic methods". There is also some similar discussion of the original generics proposal, with language like "then it's much less clear why we need methods at all". (I'm omitting some context, but I don't feel that it changes the meaning.) Those feel much closer to "never" than "not now".)
The post is also subtitled "A change of view".
Everyone also wanted and accepted the need for generics. It was always something they wanted to add to the language. Rob Pike never said that that kind of abstraction isn’t what golang is for. It was always just a matter of getting the design right.
Go has always been a systems language. It was one when we thought it was going to fit nicely for low-level, high performance use-cases. Given that the GC, runtime overhead, lack of control over memory layout, and other issues made it a poor fit for what we historically thought were systems language tasks, it’s still a systems language because we’ve grown to understand that the term “systems program” has always meant network middleware that shuttles around JSON and transforms it.
Dependency management too. Modules were something that nobody argued were unnecessary. None of the language developers ever claimed that “you should always build against HEAD, and if upstream breaks you, that’s a coordination problem to be solved socially”. The community didn’t need to independently invent godep, then glide, then govendor, then dep, before the core team finally shipped modules. That was just enthusiastic parallel exploration of a problem space that everyone agreed was a problem.
GOPATH was always understood to be an awkward temporary scaffold that everyone tolerated while the real solution was being designed. The single-workspace model was never defended as philosophically correct or a deliberate feature of the language. When modules arrived, everyone was simply relieved that this obvious stopgap was finally replaced.
The core team always intended to add builtins for min/max. Nobody ever told you to just write `if a > b { return a }; return b` yourself because it was “only two lines.” The fact that every Go codebase in existence had its own copy of this logic, typically buried in a file called util.go, was not evidence of anything being missing from the language.
Range was always a stopgap before iterators could be implemented. Nobody ever argued that iterators were needlessly complicated and went against the spirit of the language. The slices and maps packages provided important missing features that everyone using the language wanted.
Everyone agrees that errors were anemic from the outset. errors.Is/errors.As are nice additions but everything was Just Fine™ before they were added.
Speaking of errors, having two lines of error-handling boilerplate for every line of code is good, and right, and perfect. It’s not verbose; it’s “explicit”. But when that gets changed to be less verbose, we will all agree that it was always a pain and made reading code unnecessarily more difficult and that everyone always expected this to be fixed some day.
I personally can’t wait to see what next development will never have been “against the Go philosophy” and definitely not something that gophers argued was perfect the way it was any time misguided malcontents and rabble-rousers wrongly tried to suggest the language wasn’t perfect the way it was.
If I were uncharitable I might call the categories "people who are somewhat removed from reality" and "people who inhabit observable reality". Avoid the former, treasure the latter.
Languages evolve for a reason and nobody should give a shit about people who do not understand why.
The writing is on the wall for next development.
“For the foreseeable future, the Go team will stop pursuing syntactic language changes for error handling. We will also close all open and incoming proposals that concern themselves primarily with the syntax of error handling, without further investigation.”
And of course, it was replaced with a more correct implementation that was incompatible with that awful stopgap because semantic correctness trumps all. vendor/ trees and GOPATH were never meant to be remotely compatible, and don't you know -- the Go compatibility guarantee(TM) doesn't apply to misuse of GOPATH to work around shortcomings^Wwell-considered designs of Go, even if it breaks the largest Go project at the time!
(/s It still shocks me that they decided to drop "src" from vendor/src and break compatibility when they finally got around to supporting vendoring despite every tool using it. And symlinks don't work because Plan 9 is the future!!)
Who are we that has always defined that term that way. For any systems programmer golang has pretty much not been a solution.
Systems is below layer 4 of the network stack, it is building the network stack in the first place.
[0] https://github.com/google/netstack
I really hope it is more ergonomic error handling. Or maybe sum types/discriminated unions.
They've publicly said no more language changes specifically for better error handling are coming, it will at most be the library-level improvements.
Let's get this straight. I'll give you a long quote from Rob Pike's article where he describes the history of the go language:
""" One thing that is conspicuously absent is of course a type hierarchy. Allow me to be rude about that for a minute.
Early in the rollout of Go I was told by someone that he could not imagine working in a language without generic types. As I have reported elsewhere, I found that an odd remark.
To be fair he was probably saying in his own way that he really liked what the STL does for him in C++. For the purpose of argument, though, let's take his claim at face value.
What it says is that he finds writing containers like lists of ints and maps of strings an unbearable burden. I find that an odd claim. I spend very little of my programming time struggling with those issues, even in languages without generic types.
But more important, what it says is that types are the way to lift that burden. Types. Not polymorphic functions or language primitives or helpers of other kinds, but types.
That's the detail that sticks with me.
Programmers who come to Go from C++ and Java miss the idea of programming with types, particularly inheritance and subclassing and all that. Perhaps I'm a philistine about types but I've never found that model particularly expressive.
My late friend Alain Fournier once told me that he considered the lowest form of academic work to be taxonomy. And you know what? Type hierarchies are just taxonomy. You need to decide what piece goes in what box, every type's parent, whether A inherits from B or B from A. Is a sortable array an array that sorts or a sorter represented by an array? If you believe that types address all design issues you must make that decision.
I believe that's a preposterous way to think about programming. What matters isn't the ancestor relations between things but what they can do for you.
That, of course, is where interfaces come into Go. But they're part of a bigger picture, the true Go philosophy. """
Rob Pike, 2012
I can draw a few conclusions from this: firstly, he didn't want to add generics at all because he didn't think they were useful, and secondly, he doesn't understand programming very well and doesn't know what generics are and confuses them with inheritance.
The ease of grokking Go (both reading and writing) are big advantages, and facilitated by the "small" feature set of Go.
The slow turtle wins the race against the overly eager rabbit... so I'm okay with that
Like what?
But that's the thing, it's just IMO.
nah I'm kidding
<after 55 seconds>
Seriously, what's wrong with `#define`?
It'll be interesting to see the next language that comes along rejecting bloat in favor of simplicity, and then we can all start again.