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.
> Why would it be difficult to retrofit? It’s just a new set of types. Is it that existing APIs wouldn’t be using them?
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.
One problem beyond the technical "How" is that culturally Go didn't have Sum types, so there are existing APIs where, if a Sum type was possible that's what you'd do, but it wasn't, so they didn't.
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.
"I've been thinking something like sealed interfaces would fit better:"
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.
I'm not quite sure what you're trying to express, because all I'm reading in your comment is "you can get none of the benefits today with no changes", which doesn't seem like a very interesting statement? I don't understand why you insist on quoting "sum types" when literally just mentioning them.
"I'm not quite sure what you're trying to express, because all I'm reading in your comment is "you can get none of the benefits today with no changes", which doesn't seem like a very interesting statement?"
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
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.
In all fairness, I expect that Go did eat some of Java's market share (since Go is popular and targets a similar part of the market), but yeah I doubt that generics are going to make a real difference in that direction; I expect that (mostly) anyone who was going to switch has done.
well cobol hasn't gone anywhere so I don't expect Java to go anywhere. Although I dislike Java I think lots and lots of legacy apps are made in Java and isn't going anywhere. And many university still teaches Java which mean it is just as strong. And andriod etc relies on Java which means it will last for centuries.
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."
Generics will be nice, and fuzzing is great too. I'm not a huge fan of Go itself, but it works well for my use case, websites and CLI tools. The integrated tooling (test, fuzz, race detector, benchmark, format, vet, package management, building) is very nice.
Something I don’t see the Go team get enough praise for is their ability to bring major language changes like generics into the language in an actually backwards compatible minor version update.
If nothing else, I find myself choosing Go frequently because it’s not going anywhere and it’s not changing.
I wish vendoring in deps and/or monorepos were better-supported. That's the only thing that gives me pause with Go. Everything else about it is great from a "can this easily be built a couple years from now, if it's not been touched in the meantime?" perspective, but the defaults there strongly favor future breakage and last I checked make it a huge pain to take measures to avoid that problem, which is weird.
[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.
They do appear to be addressing some of those rough edges in 1.18 with the ability to specify a working mod file separate from the normal go.mod file, but you're not wrong, vendoring has some rough edges for sure.
> Something I don’t see the Go team get enough praise for is their ability to bring major language changes like generics into the language in an actually backwards compatible minor version update.
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.
> I don't really see how that is at all praiseworthy: C# and Java already did that exact thing.
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, when someone manages to do something only two other athletes have managed before they usually still get praised for it.
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.
> You know, in sports, when someone manages to do something only two other athletes have managed before they usually still get praised for it.
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?
Waiting for half a decade is a fairly effective way to tell me at least that they didn't exactly care very much about it, but it could of course be that they cared so deeply about it that they had to wait until recently before they felt it was good enough.
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.
Arguing that generics are in "the basics" is a little disingenuous, don't you think? Go has been wildly successful without generics, and the designers of the language were quite intentional and opinionated in leaving them out.
What frustrates me a bit about this argument is that interfaces really do offer a tool to solve problems around dispatch and polymorphism. Like a lot of things in Go there are lots of opinions and tradeoffs that can’t or can’t easily be revisited by someone using the language, but it’s a very different argument to say “I don’t like the tools the language provides” than “the language doesn’t provide x, y, z”.
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.
> Arguing that generics are in "the basics" is a little disingenuous, don't you think?
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 know, in sports, when someone manages to do something only two other athletes have managed before they usually still get praised for it.
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?
Language like "self-inflicted wound" suggests there was either some strong objection to generics or that everyone was unaware they wanted them. That's not true.
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".
C# and Java have very fast compilers. A JIT is generally better for compilation speed than Go's approach, as a JIT only compiles code when it's executed. javac and csc.exe don't do any up-front code generation, while Go 6g/8g does. Besides, I don't think generics particularly affected C#/Java compilation speed.
You're making the point about _when_ they implemented generics, and I don't care about when, really.
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.
> You're making the point about _when_ they implemented generics
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.
>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.
I fail to see how that contradicts my statement in any way. If anything it supports it: by “not feeling an urgency”, against repeated prompting and advice, the go team created this migration issue for themselves, and was long forewarned of it.
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.
The actual history goes a bit differently, they already had generics working, however they weren't stable and management didn't want to delay the initial release.
Java did this in a very complicated but backwards-compatible way. (Including bytecode level compatibility.) Here is a detailed FAQ [1] devoted to the intricacies of Java generics, covering erasure, bounded and unbounded wildcards, special cases for raw types and arrays, workarounds for primitive types, interaction with inheritance, etc.
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.
Many people seem to think that the language could and should have included them from the beginning. Java followed the same exact history 15 years before Go, C# too. Many of the people that didn't think that Go should have generics may not really care or be happy that they are now in the language. That may explain the reaction.
Backwards compatible as in old code still compiles? Can you explain why that would be hard? I'm not asserting it wouldn't be, just missing something. Don't you just need to add allowable syntax that would previously raise an error?
wow I didn't know about the workspace file, replace directives are messy. It seems like I'm going to remove 149 replace directives of my own, what a nightmare.
— /^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`"
Generics will be insanely useful for me. I work in an environment that uses a huge Golang KV store for all persistence. This means we write all of our own indexing code etc. None of it is reusable because of the lack of generics, so each module has much of the same boilerplate for building second indexes, etc, often written in slightly different styles because it was written by different people.
Generics will let us build a set of tools for DB access and indexing tools which can be reused across all types.
... writing the code required to write the code needed to implement indexes is more fragile than letting the compiler do it for you, most likely. I maintain a reflect-based indexing implementation for in-memory data, and look forward to seeing what I can do with generics to get rid of reflect based overhead.
:shrug: I’ve tried doing it both ways in other languages and found that code gen is less fragile than I expected, though I needed to carefully consider the interface between generated & manually written code.
Someone is working on an "ORM" that uses codegen from proto files, which we are already using for serialization. Not sure when it will be done. Compiler level generics are just a lot quicker and easier for anyone to use without requiring a whole code generation framework
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 repeatedly fail to understand why golang proponents insist on flogging themselves into becoming human stacktrace authors. This is a solved problem the computer can do better than you, automatically, on your behalf.
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.
Not that I agree with how Go handles it, but the computer is not always better at it. It's a tradeoff, a compromise, or a catch-22.
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?
It's not the same thing. In Java and Python programs, especially Java programs, developers seems to think it's perfectly reasonable to present the end user with a 100 entries stack trace. This may be of use to the developer to debug issues but it's not a substitute for good error messages that are to be presented to the end user.
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.
Languages that bubble errors up still let you catch and categorize the errors at the top, allowing for very presentable formats for the end user ... in fact, in my experience, because they don't have to deal with tiers of pointless boilerplate, devs put the effort instead into better end user/public errors.
I say this as an ex-(for now)-Python dev, currently suffering though Go's tedious error handling.
I agree that in theory the same is possible in languages that use exceptions but in my experience laziness often wins out. The exception is caught on the level where it's suitable to terminate the stack unwinding and deal with the error from the applications point of view. At that point it's too late to add user recognizable context to the error that what Go advocates by its design.
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)
> I truly think there's a bit of Stockholm Syndrome going on here.
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.
Attributing this to stockholm syndrome is a bit convenient and reductionist.
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.
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.
Please no allocations hidden in the short syntax. I use `errors.WithMessage` / `fmt.Errorf` with `%w` often within my application's main execution flow, but I don't need a stack trace attached to every missing file, parse failure, or connection reset.
What is the (moral or performance) difference between wrapping every error you return anywhere with a filename/lineno pair, and a stack trace? If anything, the stacktrace proper probably only needs to allocate once, at the lowest level.
The specifics of it are deep enough in runtime that I've never actually looked, but it's designed as a chain to return a specific caller - it looks like -
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.
Yeah, this is fine for like "unable to contact database server" or "kafka producer timeout" where you're gonna terminate a large portion (or all) of the application at a much higher, removed level. "Lifecycle" stuff.
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.
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.
I don't actually mind writing the above and adding context, but let's brainstorm some syntax that does the same thing without being verbose. How about something like:
The try syntax proposed there and in other places returns the error unwrapped, which is a no-go for me. I wrap every error that I return from every function.
Doing some analysis, "naked" if-err-return-err shows up in about 5% of error handling cases in my team's most critical service.
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.
Error handling differs. But we try to move up metrics and logs as high as possible in the call chain. Handling an error may be a retry with a strategy, moving items to different queues, changing concurrency, potentially triggering jobs to run, feeding different events to event streams, and, of course, retuning errors to callers so they can programmatically respond.
Dreading the wave of shitty badly-thought-out library "updates" to include generics (and the wave of shitty new libraries from people who never learned how to use interfaces and now think they know how to use generics). It's going to be a year or so before the community settles down again after this.
As someone who has recently decided to learn Go, this is a rather intimidating change. What is a good way to know that I am learning from sources who are using these new tools responsibly, rather than applying them in the messy fashion which you seem concerned about?
Stick close to the standard library and the extended libraries (https://pkg.go.dev/golang.org/x ), track a community list where a lot of discussion will be occurring very quickly after release.
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.
this. Interfaces do 90% of what generics are used for in other languages. The remaining 10% had been solved by boilerplate/generation. Yes, it will be nice to have a better answer for that 10%. But we'll probably end up with generics being used (badly) for 90% of cases instead.
As always in Go, follow the standard library. If they're hesitating to use generics, then that's solid advice.
I'm hoping some linters get into golangci-lint quickly to curtail some of the biggest abuses. Some of them are obvious, like:
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.
The design does not require a specific implementation strategy (monomorphization, boxing or other approaches are possible). It can change from Go implementation to Go implementation and from Go version to Go version.
It works the other way too; the implementation is not obligated to pass an interface value to a function. The interface can be monomorphized too, if the compiler saw fit, and it would be a compliant implementation.
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.
I'm on the opposite side of that fence. I'm looking forward to seeing what wonderful libraries can be created now that we have generics. It's going to be really interesting to see who's "right". Will the fear of those who think coders can't code and everything is going to get worse outweigh the concise and expressive functionality that can show up in a well crafted generic library?
>Dreading the wave of shitty badly-thought-out library "updates" to include generics
>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."
Been a while since I worked with Go but I'm considering it for an upcoming project.
Is there any word on a new, updated version of "The Go Programming Language" (Donovan, Kernighan) to cover all the new features since then? I really enjoyed working through that book.
If not: Any tips for a good resource to really get up to date with the new additions/changes since ~2015?
I think there is a need for this. I use it daily and still find myself relegated back to pretty much the features that were available when I first picked it up - would love somewhat of a refresher book that is updated for 2021/2022.
The Go Programming Language is still pretty much up to date. Most of the recent updates have involved improvements to tooling (Go modules), improvements to the standard library, and internal optimizations.
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.
In my laptop(ThinkPad P1 Gen3 w/ Intel i7-10750H), i run the same command in WSL2 env after executing `make`:
real 0m5.321s
user 0m25.747s
sys 0m3.276s
So i doubt if macOS has some magic for Intel CPU to make it slow?
115 comments
[ 2.4 ms ] story [ 181 ms ] thread* 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:
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
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.[0] https://github.com/BurntSushi/go-sumtype
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."
https://www.microfocus.com/en-us/what-is/cobol
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...
— -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`"
Generics will let us build a set of tools for DB access and indexing tools which can be reused across all types.
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...
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.
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.
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.
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.
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."
[0] https://github.com/golang/go/issues/48918
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.
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.