Upcoming Features in Go 1.18
sebastian-holstein.de
sebastian-holstein.de
If nothing else, I find myself choosing Go frequently because it’s not going anywhere and it’s not changing.
[EDIT] oh, they added a command for that, "go mod vendor", I guess, and I just missed it somehow. Nice.
[EDIT EDIT] On further reading the feature's practically built of rough edges and you're probably gonna want 3rd party tools to manage it if you value your sanity. Hmph. Well, that sucks.
I don't really see how that is at all praiseworthy: C# and Java already did that exact thing. Plus the entire peanut gallery has been saying generics were necessary from the very first announcement (or at least the first where readers realised there were no generics) so this is a completely self-inflicted wound.
You know, in sports, when someone manages to do something only two other athletes have managed before they usually still get praised for it. I don't know if this situation is comparable to that, but my point is that I also don't see how C# and Java doing this before inherently would make this less of an achievement. Perhaps we haven't been giving those languages the praise they deserve for doing that too.
You know, in sports, if you kept straddle-jumping until the 80s before finally transitioning to the fosbury like everyone told you for more than a decade, you wouldn't get praised for it.
> I don't know if this situation is comparable to that, but my point is that I also don't see how C# and Java doing this before inherently would make this less of an achievement.
Because they demonstrated it was quite reasonably feasible before Go even existed.
And also because, again, Go's situation was entirely predictable and self-inflicted, and C# and Java's own transitions demonstrated it plainly.
This is more like solving a puzzle years after others proved it solvable and after having told everyone multiple times that it didn't matter, isn't it?
I don't think so however and here is what Russ Cox wrote about it in 2017:
> For example, I’ve been examining generics recently, but I don’t have in my mind a clear picture of the detailed, concrete problems that Go users need generics to solve. As a result, I can’t answer a design question like whether to support generic methods, which is to say methods that are parameterized separately from the receiver.
(from https://go.dev/blog/toward-go2)
At least the way I read the above it comes off as "doesn't matter".
There were also plenty of successful languages without generics in the last century, that doesn't mean we should keep designing them.
IMHO there are convincing arguments that Go needs x, y, z (or: prefer another language if you need x, y, z). A good example of one of these that is being addressed would be priority queues. Doing them via interfaces was always possible - there’s a whole std lib package for them. Generics will be better. But looking at the pre-/post-generic ways of doing priority queues confirms and emphasizes, rather than negates, the utility of interfaces as a way of structuring code. It’s still opinionated and I think it’s fair to argue about alternatives but it’s absolutely not the case that some large class of reasonable problems was entirely ignored or forgotten.
Not really. Type polymorphism exists since the 70's, it still blows my mind that people consider that they are ‶too hard to understand for developers″, or that ‶their implementation is not very clear to language designers″.
> Go has been wildly successful without generics
JS have been wildly successful despite its many fundamental flaws, success is not an indicator of intrinsic quality.
You're talking about it like it's a time in 100m or something, while many people seem to see it as "taking less than 30 seconds to get 5 apple out of a barrel of water with your mouth, hands tied behind the back, blindfolded". Cool, but why didn't you release the generics from the beginning, especially when Java and C# added them?
There's never been a strong design opposition to generics among the Go developers (note "the X developers" as distinct from the weird fanboy communities today of "X developers"), but it's always been balanced against other priorities, particularly compiler speed. C# and Java aren't quite so ready to throw stones in that area.
That being said, I don't think it's some uniquely praiseworthy thing either. For me that's actually more for things like `strings.Cut`; too many languages refuse to use their stdlib as a tool to push good design, especially good "micro-design".
I care about a stable language that can still move and change when necessary without breaking everything behind it, and Go delivers that.
It's non-trivial to design and add a feature as core to a language as generics and make it a backwards compatible release, and you're glossing over that by saying they should've done that sooner.
The matter of timing is one that the Go team has a lot of their own thoughts around, so I would defer to them on that, but as for the quality of their releases, I think they deserve more praise.
I'm not, I'm pointing out that there was nothing actually exceptional or praiseworthy about it.
> It's non-trivial to design and add a feature as core to a language as generics and make it a backwards compatible release
So?
> and you're glossing over that by saying they should've done that sooner.
I'm glossing over that by saying that it's hardly novel, and that this is a self inflicted issue which dates back literally to the original language. This is a problem they were told about all along. They never had to take that risk, they decided to against advice.
They were always open to adding generics to the language, as can be seen in their FAQ from 2013 [0]:
>Why does Go not have generic types? Generics may well be added at some point. We don't feel an urgency for them, although we understand some programmers do. Generics are convenient but they come at a cost in complexity in the type system and run-time. We haven't yet found a design that gives value proportionate to the complexity, although we continue to think about it.
[0] http://web.archive.org/web/20130118182924/https://golang.org...
And that faq entry is little more that a way to tell critics to shut up about it. Being added in 2013 means people had been telling them about the issue for more than 3 years.
>I don't really see how that is at all praiseworthy: C# and Java already did that exact thing.
Not quite true for C#: generics were added in CLR 2.0, which was a major update from CLR 1.1; the runtime was considerably reworked.
https://mattwarren.org/2018/03/02/How-generics-were-added-to...
The two are unrelated.
It was a big achievement, but also something to learn from. It’s reasonable that other language designers would want to do better.
The C# designers tried to improve on what Java did, reconsidering many of their design decisions [2]. Apparently it was a five year effort, including inventing novel runtime mechanisms. They had different goals, particularly supporting multiple languages and cross-language compatibility.
Go’s generics support is another years-long effort. They reconsidered everything again in a new context, because the Go language doesn’t have the same features or design constraints. That’s also a big achievement.
This only looks like the “exact same thing” if you ignore all the history. Language design is all about the details. Every language feature interacts with every other language feature.
The “peanut gallery” is a lot of random people, some of whom don’t know anything about designing or maintaining a production language. If you really want to know about this stuff, I suggest reading the papers by people who actually did it.
[1] http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.ht... [2] https://mattwarren.org/2018/03/02/How-generics-were-added-to...
if x,err = f(); err!=nil { return _,err }
pattern that is so common in go code. This is my biggest daily gripe and I work around it with a snippet but so much of integration code is this or a variant of it which captures the stack trace and it constantly distracts from the main flow of algorithm.
I am tired of seeing
SocketException: hostname.com
as full error message.
Certainly any shortcut syntax should not prevent you from adding human-curated context to your errors. But the current situation comes at an enormous cost, and I truly think there's a bit of Stockholm Syndrome going on here.
If you use the full stacktrace, you could get 50 frames of irrelevant library code worsening the signal:noise ratio, hiding what the real issue is.
If you write your own context, then when it _is_ a problem with library code, you're hopelessly lost.
I think we need better tooling: the ability to add your own contextual information, to propagate errors explicitly but without boilerplate, and the ability to _choose_ the level of information you see afterward.
We have log viewers that can filter out by severity level, why don't we have a standardization for stack traces to let you filter to what you want?
I don't think Go errors are perfect either as you just tend to concatenate strings together but the information they include are more user friendly if done right, i.e. not just bubble up the error, but adding context that the user is aware of.
I say this as an ex-(for now)-Python dev, currently suffering though Go's tedious error handling.
My opinion is that a syntax shortcut such as "?" to just bubble up errors without adding context would still be useful. I would welcome it. There are cases where I find that no additional context is needed. I think the Go designers are afraid what it would lead to though (the laziness wins out hypothesis)
Try debugging something in Spark/DataBricks. It is so incredibly tedious; stack traces can easily reach 200 lines or more.
I agree in sense Stockholm Syndrome going on but not just for Go but with almost every technology including Go.
When we have folks defending Rust slow builds, Swift breaking changes, Half-assed tooling for Scala, poor power management on linux laptops and on and on. It could mean either plain old stockholm syndrome or that people can work around these minor irritants and get main benefit of technology. Just like with anything else in life.
Stacktraces are better than just bubbling up, and yes a mechanism to minimize if-err could also allow for human annotations.
I'd love if Go had a stronger type system, but I know it'd probably come with longer compile times like Rust has. I think it's not a dogmatic take, and I think a lot of golang proponent do take the same nuanced position.
[0] https://ziglang.org/documentation/master/#Error-Return-Trace...
x, err := f(); if err != nil return fmt.Errorf("More context -> %s", err)
(I'm not attached to that specific syntax, it's just an example of a hypothetical one-liner)The pattern itself as written above is rather useless. What you end up with is a call 3 libraries deep that returns nothing but "EOF", and you have no idea what happened. In practice, you should really wrap every error return in a message and/or with a callstack. If the shortcut method can a) accomplish that, and b) avoid everything being nested, like try/except tends to do, I think I'd be more interested in the concept.
_, file, line, _ := runtime.Caller(2)
locTxt := fmt.Sprintf(" (at %s:%d)", filepath.Base(file), line)
I think if the above on errors became a performance concern, I'd heavily rethink what I considered an error. But, it is also possible the systems we've designed are miles apart in function and philosophy.But if you make the short syntax do something like this, it's gonna get used for hundreds of library's equivalents of `Atoi` or `ParseQuery`. I don't need line annotations on my `http.ResponseWriter#Write` timeouts, they're slow enough already.
(And if you are going to do it - really, just attach the whole stack trace then and there, it will probably be faster.)
Errors reach end users, and they cannot read stack traces.
Every shortcut makes for bad errors, bad troubleshooting and unhappy users.
Errors document code (you literally write, in English, what you were trying to do but failed, at each level of your program.)
Errors report to the user the clear intent that failed and why.
Errors are a huge differentiator in quality. There is no shortcut to quality.
In general, users don't care about errors or stack traces. They don't want errors of any kind. As a developer I want users reporting errors, which can include a dense stack trace, so I can get the error and an explanation of what they were doing when the error happened. I'm more for making my life easier than worrying about what a user sees.
$variable1 := try $function_call_which_returns_two_values except $variable2: $expression_which_has_error_type
which is automatically desugared to $variable1, $variable2 := $function_call_which_returns_two_values
if $variable2 != nil {
return ($zero_values_for_other_return_types, )* $expression_which_has_error_type
}Don't forget to check if someone else already had the same idea: https://seankhliao.com/blog/12020-11-23-go-error-handling-pr...
The rest of the error blocks do something more (attempt a fallback, add context, log something, metric something, etc). This is code base that has been worked on for about 6 years, around 40k lines of code, 32 contributors, and over 450 releases. This is a legit production mail transfer agent. Take this as an experience report on a real system: that pattern is not as common as you think. 5% is pretty low.
* sum types (would be really nice)
* some equivalent of ? in Rust (can live without but would be nice)
After playing with a ton of languages have settled on Go for most projects.
That seems really difficult to retrofit, and to not really fit the language either.
I've been thinking something like sealed interfaces would fit better: Go already has fallible downcasts (and RTTI which takes the role of discriminant), and type switches can play the role of your `case` statement:
func do[T any](i option[T]) {
switch v := i.(type) {
case T:
fmt.Printf("Got a %v", v)
case nothing: // or `nil` could be a (pseudo)-type
fmt.Printf("was empty!")
// no default necessary because the check is complete
}
}
The only pieces missing are a way to "seal in" a set of types, and add completeness check to type switches for those.It's a brand new kind which wouldn't necessarily work the way existing types do, and it's a whole lot of extra syntax.
Plus previous retrofits have not exactly panned out great, C++ and Java's native "type safe enumerations" are pretty crummy as they're closer to enumerated sets of constants than sum types.
Sealed classes/interfaces/traits/… fit well in the machinery and aesthetics of RTTI-heavy products-types-based languages and slot nicely into the "niche" of sum types.
This isn't so different from the problem Generics introduce (why isn't this API generic, oh, it pre-dates that feature). And it might similarly be worth it for Sum types, but it's certainly an extra consideration.
On the technical side, Sum types kinda suck unless you have niches. If your Optional sum type is always actually bigger than the Some type it is wrapping then why even bother? So then you're also adding promises about niches in your implementation.
You can have "sealed interfaces" today; you put an unexportable method name in the interface. Then no legal external implementation can exist. (Not even if someone "guesses" the method name; the compiler will not consider them to have the same name.)
What you still don't get is completeness checking even so. In theory a linter could do it even without compiler support, I don't know if one exists.
You can use this in Go today to get, oh, say, 1/3rd of the feature of 'sum types' today. But you don't get much of the "sum types" bang for that 1/3rd of the buck. Basically, you can use them, and you don't need to do the fully manual type of thing you need to do in C with unions and tags, you can lean on the interfaces carrying type information around to handle that, but you get no additional compiler or syntax support, nor any sort of pattern matching on them.
You can get some of the benefits today with no changes to Go. You can create a package-level sealed interface: http://www.jerf.org/iri/post/2917
"I don't understand why you insist on quoting "sum types" when literally just mentioning them."
Because they aren't literally sum types, in that they have every feature that everyone associates with sum types, and people are very sensitive about that. So I don't need anyone replying with "but those aren't really sum types", either for my previous HN post or the blog post I just linked. Yes, I know they don't check every check box. But they do check a few of them, and, arguably the most essential one which is that you can indeed implement
type ASumType interface {
unexportedMethod()
}
func SomeFunction(thing ASumType) { ... }
and ASumType can be any of several types, closed and limited at compile time to only be things defined in this particular package in a way that can not be extended by any other external package.I don't want to recommend "don't use generics", but generally think about whether or not you can solve the problem without them without immediately reaching for them. There are patterns in Go that work just fine today, which is why despite the fact that clearly some people are just flabberghasted at the idea that Go is useful without generics, it demonstrably is. Interfaces are quite a lot of what you need out of "generics". Don't just give up and copy and paste; it is necessary way less than some people who were trying to write C++ in Go claim it is.
As always in Go, follow the standard library. If they're hesitating to use generics, then that's solid advice.
func Whatever[reader io.Reader](r reader) { ... }
You can tell just from the type signature that that is a useless use of generics. If it returned "reader", then that would be potentially useful, but as written that is useless.There will be some other things that can be caught too, potentially.
It's… not, though?
In my understanding Go generics are reified, so this avoids boxing the reader, and should allow for static dispatch.
How useful that is can be debated, but that's hardly useless.
At the language level, it's a useless use of generics. Even if generics someday used a monomorphic implementation, the compiler would be able to translate those two things back and forth trivially.
>It's going to be a year or so before the community settles down again after this.
Rob Pike doesn't want to add generics to the standard library in this release (1.18) for the same reason[0]:
"<..>we have no experience with the use of the new types in Go on which to base a strong case for their design <..> For generics, we don't know what those new ways are yet. <..> I realize everyone wants to get their hands on the fun of the new language feature, and is looking forward to fixing some of the issues in the core libraries that will be less clumsy once it arrives, but I strongly believe it is best to take it slow for now."
Generics will let us build a set of tools for DB access and indexing tools which can be reused across all types.
If not: Any tips for a good resource to really get up to date with the new additions/changes since ~2015?
The big changes I'd point out are `context.Context` and `go mod`. I might be forgetting some things, but those are most notable to me.
grep -rnw '.' --include=\*.mod -e 'replace' | grep "" -c
149 find . -name go.mod | xargs gawk -i inplace '/^replace \(/{d=1}; (!d && !/^replace/); /^\)/{d=0}'
Explanation of gawk arguments:— -i inplace — https://stackoverflow.com/a/16531920
— /^replace \(/{d=1} — on lines starting with `replace (`, set a variable named `d` (any var name would work, 'd' is my mental shortcut for 'delete' here) to value `1` (i.e. "true"); Note: in awk, unset variables default to 0 ("false") value
— (!d && !/^replace/) — on lines where `d` is 0 (which is default value of unset variables in awk), and which don't start with `replace` pattern, print the line. Default action when unspecified in awk is `{print}`, this entry is a shortcut of: (!d && !/^replace/) {print}
— /^\)/{d=0} — on lines starting with `)`, reset `d` variable back to 0 ("false")
In other words, this encodes: "delete any lines between `replace (` and nearest `)`, and also delete any other lines starting with `replace`"
This sounded extremely slow to me - go fmt is normally instant. Granted, cockroachdb is a big project
But I had to download and try, and on my computer (Ryzen 5950X) it only took ~3.1s on Go 1.17. I didn't bother trying tip.
Java will fade out as old systems fade out in a few decades as ecosystems catch up in other languages.
I'm kind-of puzzled how we'd even start to make such a comparison, given that "the market" for computer software has been incredibly diverse for decades.
Explosive growth in parts of the market that previously didn't exist, slower growth in parts that existed for many decades.
"Modernization was favored over the replacing and retiring of older systems with 63 percent of respondents choosing to improve upon their existing COBOL systems in 2020."