Live data from Hacker News

Context should go away for Go 2 (2017)

faiface.github.io

141–150 of 176 posts

Re: Context should go away for Go 2 (2017)

#141
post #97
post #86

Earlier quoted context omitted.

No. The point of rust editions is that they do break support for older code, which is very different to what go has now settled on. IMO, it's the best of both worlds. Old code continues to work forever, but your language design isn't held back by older design mistakes.

The trouble with the Rust community is that it is terrible at communication. That may be why you extend a presupposition that everyone understands the meaningful difference between Rust editions and Go version directives, but I can't tell a difference beyond the frivolous like syntax used. Based on the documentation of each they seem like the exact same concept, with the exact same goals in mind. As a result, unfortu…

Editions allow making breaking changes to Rust without splitting the ecosystem - no hassle caused to existing code unless it opts into the new edition and its breaking changes. There's currently editions 2015, 2018, 2021, and 2024. When a new edition is introduced, it can make breaking changes such as introducing new keywords, but every previous edition remains supported forever by newer compiler versions.

The key part is that editions are configured per-library - libraries A and B might use editions 2015 and 2021, and your application could use edition 2018 and depend on those libraries, and it works.

If you wrote a library with the original 2015 edition, and never upgraded it to deal with new `async` and `await` keywords added in the 2018 edition, that's totally fine. Newer compilers will continue to compile it in its configured edition=2015 mode, where the new keywords don't exist (so your local variable named `async` still compiles), and new code written against newer editions may still use this 2015 edition library with no issue.

Editions are different from Go version directives because you use them to say "my library needs features added in this Go version", but they don't enable Go to make breaking changes to the language.

Editions can't do every kind of breaking change however - they mostly work for syntax level changes, and don't work for things like tearing out regrettable parts of the standard library.

Re: Context should go away for Go 2 (2017)

#142
post #100
post #92

Earlier quoted context omitted.

> an implicit context variable that allows you to pass stuff deep down the call stack, without intermediate functions knowing about it. [...] but is actually very useful and can result in cleaner code with less globals or less superfluous function arguments. [...] and it applies to everything down the call stack (but not in other threads/async contexts). In my experience, these "thread-local" implicit contexts are a…

They're basically dynamic scoping and it's both a very useful and powerful and very dangerous feature ... scheme's dynamic-wind model makes it more obvious when the particular form of magic is in use but isn't otherwise a lot different. I would like to think that somebody better at type systems than me could provide a way to encode it into one that doesn't require typing out the dynamic names and types on every singl…

Basically you'd be asking for inferring a record type largely transparently. That's going to quickly explode to the most naive form because it's very hard to tell what could be called, especially in Go.

Re: Context should go away for Go 2 (2017)

#143
Consider what happens in JavaScript when you declare a function as async. Now everything calling it is infected. Passing around runtime constructs like context in Go (AbortSignal in JS) or an allocator in Zig gives exactly the right level control back to the call and I love it. You can bail out of context propagation at any level of your program if that's your desire.

Re: Context should go away for Go 2 (2017)

#144
post #97

Earlier quoted context omitted.

The trouble with the Rust community is that it is terrible at communication. That may be why you extend a presupposition that everyone understands the meaningful difference between Rust editions and Go version directives, but I can't tell a difference beyond the frivolous like syntax used. Based on the documentation of each they seem like the exact same concept, with the exact same goals in mind. As a result, unfortu…

Editions allow making breaking changes to Rust without splitting the ecosystem - no hassle caused to existing code unless it opts into the new edition and its breaking changes. There's currently editions 2015, 2018, 2021, and 2024. When a new edition is introduced, it can make breaking changes such as introducing new keywords, but every previous edition remains supported forever by newer compiler versions. The key pa…

> The key part is that editions are configured per-library - libraries A and B might use editions 2015 and 2021

In what way is that key? It still reads as being the same as the Go version directive. Obviously there are some differences in the implementation. For example, Go puts it in go.mod, while Rust puts it in Cargo.toml, but at a conceptual level I fail to see any fundamental difference. As you describe it, and how the documentation describes it, they attempt to accomplish the same thing for the same reason.

But, as phire puts it, they are "very different". But I don't see how. The carrying on of the tradition of the Rust community being horrible at communication carries on, I'm afraid. As before, you are going to have to speak to those who aren't deep in the depths of programming languages. Dumb it down for the reader who uses PHP and who has never touched Go or Rust in their life.

> they don't enable Go to make breaking changes to the language.

What, exactly, do you mean? The change to loop variable semantics comes to mind that was clearly a breaking change to the language, but gracefully handled with the version directive. What purpose are you under the impression the directive serves if not for dealing with breaking changes?

Re: Context should go away for Go 2 (2017)

#145
post #125

Earlier quoted context omitted.

Sorry to have disappointed you. I don't walk, talk, or sleep either Rust or Go, but was trying to provide some resources to help in case you hadn't seen them yet. One difference I noticed in the docs is in the Go Reference it says the "go" line of the "go.mod" has to be greater than or equal to the go line of all that modules dependencies, if the go line is 1.21 or higher, so a module for 1.21 can't depend on a modul…

Thanks for trying. But it is the "which is very different to what go has now settled on" that we are trying to get to the bottom of. It appears from your angle that you also conclude that Go has settled on the very same thing, frivolous implementation details aside. Hopefully phire will still return to dumb it down for us.

Ok... the only thing that go version directives do is selectively enable new features. Essentially, they are only really there to help you ensure your code will continue compiling in older versions of the compiler. Hell, until recently it wouldn't even throw an error if you tried to compile code with a future version directive.

The actual backwards compatibility in go is achieved by never removing functionality or syntax. New versions can only ever add new features/syntax. If there was a broken function in the API, that function needs to stick around forever, and they will be forced to add a second version of that function that now does the correct thing.

So, you can take code written for go 1.0, slap on a "go 1.23" directive and it will compile just fine. That's the guarantee that go provides. Well, mostly. There are a few examples of go 1.0 code that doesn't compile anymore, even when you use a "go 1.0" directive.

But not being able to remove anything ever is limiting.

A good example of how this can be limiting is reserved keywords. Go has a fixed set of reserved keywords that they picked for 1.0, and they can never reserve any more, any code using them as identifiers will break. Any new feature needs to be carefully designed to never need a new reserved keyword. Either they reuse an existing keyword (which c++ does all the time), or they use symbols instead.

But rust can reserve new keywords. The 2018 edition of rust reserved "async" and "await" for future async functionality and "try" for a potential try block. Rust did reserve "yield" from the start for generators, but decided they needed a way to mark a function as a generator, so in the 2024 edition, "gen" is now a reserved keyword, breaking any code that uses gen as a function/variable name.

Do note that rust also follows the go strategy within an edition. There is only one new edition every three years, and it would be a pain if all new features had to wait for a new edition. So the async/await keywords were reserved in the 2018 edition, but didn't actually get used until the end of 2019.

This means that just because your rust version supports the 2018 edition, doesn't mean it will compile all 2018 code. The editions are just for breaking changes, and there is a seperate minimum "rust-version" field that's somewhat equivalent to go's "go 1.x" directive. Though, "rust-version" doesn't disable features, it's just there to provide a nice clean warning to users on old compilers. Ideally in the future it will gain the ability to selectively disable language features (as rust already has extensive support for selectively enabling experimental language features in nightly builds, which we haven't even talked about here).

Basically, rust editions allow all breaking changes to be bundled up and applied once every three years. They also provides a way for compilers to continue supporting all previous editions, so old code will continue to work by picking an old version. While go's version directive looks superficially similar to editions, it is there for a different reason and doesn't actually allow for breaking changes.

Re: Context should go away for Go 2 (2017)

#146
post #37

> If you use ctx.Value in my (non-existent) company, you’re fired This is such a bad take. ctx.Value is incredibly useful for passing around context of api calls. We use it a lot, especially for logging such context values as locales, ids, client info, etc. We then use these context values when calling other services as headers so they gain the context around the original call too. Loggers in all services pluck out v…

Maybe he doesn't have a company because he is too dogmatic about things that don't really matter.

Someone has to question the status quo. If we just did the same things there would be a lot less progress. The author took the time to articulate their argument, and publish it. I appreciate their effort even if I may not agree with their argument.

Re: Context should go away for Go 2 (2017)

#147

> It’s very similar to thread-local storage. We know how bad of an idea thread-local storage is. Non-flexible, complicates usage, composition, testing. I kind of do wish we had goroutine local storage though :) Passing down the context of the request everywhere is ugly.

I like explicit over implicit. I will take passing down context (in the sense of the concept, not the specific Go implementation) explicitly everywhere over implicit ("put it somewhere and I'll trust I can [probably, hopefully] get it back later") any day of the week. I've seen plenty of issues in Java codebases where there was an assumption some item was in the Thread Local storage (e.g. to add some context to a log…

Most recently ive been bit by this with datadog. The Python version does some monkeypatching to inject trace info. The go version you need to inject the trace info explicitly. While the latter takes more setup, it was much easier to understand what was going on and to debug when we ran into issues.

Re: Context should go away for Go 2 (2017)

#148
post #145
post #125

Earlier quoted context omitted.

Thanks for trying. But it is the "which is very different to what go has now settled on" that we are trying to get to the bottom of. It appears from your angle that you also conclude that Go has settled on the very same thing, frivolous implementation details aside. Hopefully phire will still return to dumb it down for us.

Ok... the only thing that go version directives do is selectively enable new features. Essentially, they are only really there to help you ensure your code will continue compiling in older versions of the compiler. Hell, until recently it wouldn't even throw an error if you tried to compile code with a future version directive. The actual backwards compatibility in go is achieved by never removing functionality or sy…

> The actual backwards compatibility in go is achieved by never removing functionality or syntax.

The previous version removed functionality related to loop variables, so that is not strictly true. You might be right that the project doesn't take change lightly. There has to be a very compelling reason to justify such change, and why would it be any other way? If something isn't great, but still gets the job done, there is no reason to burden developers with having to learn a new language.

Go is not exactly the most in-depth language ever conceived. There is not much functionality or syntax that could be removed without leaving it inoperable. But there is no technical reason why it couldn't. The mechanics to allow it are already there, and it doesn't even violate the Go 1 guarantee to do so under the operation of those mechanics.

So, sure, it is fair to say that there is a social reason for Go making as few breaking/incompatible changes as is possible, but we were talking about the technology around allowing breaking/incompatible changes to co-exist and how the same concept could have been applied to Python. In Rust community fashion, I am not sure you have have improved on the horrible communication. We recognized that there is some difference in implementation details right from the onset, but the overall concept still seems to be the same in both cases to me.

Again, we need it dumbed down for the every day average person. Your audience doesn't eat monads for breakfast like your expression seems to believe.

Re: Context should go away for Go 2 (2017)

#149
post #50

Earlier quoted context omitted.

> `ctx.Value` is an `any -> any` kv store that does not come with any documentation, type checking for which key and value should be available. On a similar note, this is also why I highly dislike struct tags. They're string magic that should be used sparingly, yet we've integrated them into data parsing, validation, type definitions and who knows what else just to avoid a bit of verbosity.

Most popular languages support annotations of one type or another, they let you do all that in a type safe way. It's Go that's decided to be different for difference sake, and produced a complete mess.

IMO Go is full of stuff like this where they do something different than most similar languages for questionable gains. `iota` instead of enums, implicit interfaces, full strings in imports (not talking about URLS here but them having string literal syntax), capitalization as visibility control come to mind immediately, and I'm sure there are others I'm forgetting. Not all of these are actively harmful, but for a language that touts "simplicity" as one of its core values, I've always found it odd how many different wheels Go felt the need to reinvent without any obvious benefit over the existing ones.

Re: Context should go away for Go 2 (2017)

#150
post #148
post #145

Earlier quoted context omitted.

Ok... the only thing that go version directives do is selectively enable new features. Essentially, they are only really there to help you ensure your code will continue compiling in older versions of the compiler. Hell, until recently it wouldn't even throw an error if you tried to compile code with a future version directive. The actual backwards compatibility in go is achieved by never removing functionality or sy…

> The actual backwards compatibility in go is achieved by never removing functionality or syntax. The previous version removed functionality related to loop variables, so that is not strictly true. You might be right that the project doesn't take change lightly. There has to be a very compelling reason to justify such change, and why would it be any other way? If something isn't great, but still gets the job done, th…

> Go 1.22, for example, removed functionality related to loop variables, so that is not strictly true.

Ah, interesting. I searched but I couldn't find any example of go actually making a breaking change. Rust has a massive document [1] documenting every single breaking in single document. With go you kind of have to dig through the release notes of each version.

So, maybe Golang are relaxing their stance slightly on backwards compatibility, now that they have a mechanism that does kind of work. Which is good, I encurage that. But their offical stance is still that most code from go 1.0 should work without issues.

> there is no reason to burden developers with having to learn a new language.

To be clear, many of the breaking changes in Rust editions are the same kind of thing as that go loop example. Edge cases where it's kind of obvious that should have always worked that way, but it didn't.

The average programmer will barely notice the changes between editions, they won't have to re-learn anything. The major changes to the language usually come in regular feature releases; They are optional additions, just like go.

While the edition mechanism could be used to make changes as big as Python 3, so far it hasn't and I don't think it ever will. Decent chance there will never be a "rust 2" either.

> In Rust community fashion, I am not sure you have have improved on communicating the difference

Sigh... you aren't wrong:

----------

The difference is more attitude than anything else.

Go strives to never have breaking changes, even if they are forced to bend that rule sometimes. They do have a mechanism that allows breaking changes, but seems to be a reasonably recent innovation (before go 1.21, it wouldn't even error when encountering a future go version directive, and they didn't use it for a breaking change until 1.22)

Rust accepts that it needs to do breaking changes sometimes, and has developed mechanisms to explicitly allow it, and make it as smooth as possible. And this mechanism works very well.

----------

BTW, I'm not even saying go's stance is wrong. Rust needs this mechanism because it's a very ambitious language. It never would have reached a stable 1.0 unless it recognised the need for breaking changes.

Go is a much simpler language, with a different target market and probably should be aiming to minimise the need for breaking changes.

My original point is that "never make breaking changes" is the wrong lesson to take away from Python 3. And that Rust's editions provide a very good example of how to do breaking changes correctly.

[1] https://doc.rust-lang.org/edition-guide/editions/

Post reply on HN