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...
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?!"
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.
Not OP, but see the entire section on metaprogramming: https://mojolang.org/nightly/docs/manual/metaprogramming/. Mojo's compile-time programming is influenced by Zig, but has features that are not in Zig e.g traits, generics (real generics), and constraints, that work together.
That's fair, I think I should have just said "comparable to Zig". The type'd ness is what I was thinking of, but having actually written some zig in the last few days to play with their Io model / see what passing around an allocator is like, Zig is pretty fantastic.
I still prefer the structure in Mojo, but boy do I miss if/switch as expressions.
recover()'s semantics make it so that "pointless" use like this can be inlined in a way that changes its semantics, but "correct" use remains unchanged.
Yes, maybe some code uses recover() to check if its being called as a panic handler, and perhaps `go fix` should add a check for this ("error: function to be inlined calls recover()"), but this isn't a particularly common footgun.
package main
//go:fix inline
func foo[T [8]byte | [4]uint16]() {
var v T
var n byte = 1 << len(v) >> len(v)
if n == 0 {
println("T is [8]byte")
} else {
println("T is [4]uint16]")
}
}
func main() {
foo[[8]byte]()
}
Go was my favorite language for a long time, and I have written many books and articles about it. However, since the release of Go 1.22 [1], that is no longer the case. Go 1.22 damaged Go's reputation for promoting explicitness and maintaining strong backward compatibility.
for-loop variable capture was maybe the #1 worst decision in the language. It was never what you wanted. I appreciate Go's commitment to backwards-compatibility, but in this case breaking it was the right choice.
for i, p := 0, (*int)(nil); p == nil; fmt.Println(p == &i) {
p = &i
}
Be honest, how many times have you actually seen code which depended on the address of a variable declared in a 3-clause for-loop remaining stable across loop iterations? Nobody does this. It's extremely weird and un-idiomaic. Heck, 3-clause for-loops are somewhat uncommon in and of themselves. Conversely, everyone who writes Go has experienced unintuitive capture issues with for-range loops.
Are you actually aware of any breakages this change has caused?
this is a repeating pattern with languages; there's no commitment to design coherency, no governance, just a mad scamper to stuff features into the project until it becomes an absurd caricature of itself.
C++ did it so egregiously & disastrously you'd think language maintainers would have been scared straight. No, like moths to a flame, it is the preferred hill to die on.
This is how C99 keeps winning when it bleeping should not. It's settled science, however imperfect. It's not getting rearranged because someone read a blog post. It has stability in real-world clock-on-the-wall terms like nothing else.
The change made in Go 1.22 for 3-clause-for loops is not a new feature. It simply broke backward compatibility and old Go principles. It is much worse than C++'s stuff features.
Similar to my "Go 101" books website, about 1000 line of Go code (started from 500 lines at 9 years ago). The whole website can be built into a single Go binary.
reply