Live data from Hacker News

Context should go away for Go 2 (2017)

faiface.github.io

31–40 of 176 posts

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

#31
post #27

This is about an explicit argument of type "Context". I'm not a Go user, and at first I thought it was about something else: an implicit context variable that allows you to pass stuff deep down the call stack, without intermediate functions knowing about it. React has "Context", SwiftUI has "@Environment", Emacs LISP has dynamic scope (so I heard). C# has AsyncLocal, Node.JS AsyncLocalStorage. This is one of those id…

Passing the current user ID/tenant ID inside ctx has been super useful for us. We’re already using contexts for cancellation and graceful termination, so our application-layer functions already have them. Makes sense to just reuse them to store user and tenant IDs too (which we pull from access tokens in the transport layer). We have DB sharding, so the DB layer needs to figure out which shard to choose. It does that…

An alternative is to add all dependencies explicitly into function argument list or object fields, instead of using them implicitly from the context, without documentation and static typing. Including logger.

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

#32

> 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.

Context's spread just like exceptions do, the moment you introduce one it flies up and down all the functions to get where it needs to be. I can't help but think that local storage and operations for Go just like Threads have in Java would be a cleaner solution to the problem.

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

#33

Was this solved? Is this context only a cancellation flag or does it do something more? The obvious solution for a cancellation trigger would be to have cancellation as an optional second argument. That's how it's solved in e.g. C#. Failing to pass the argument just makes it CancellationToken.None, which is simply never cancelled. So I/O without cancellation is simply foo.ReadAsync(x) and with cancellation it's foo.R…

It's not just for cancellation and timeouts, it is also used for passing down metadata, but also for cross-cutting concerns like structured loggers.

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

#34
> First things first, let’s establish some ground. Go is a good language for writing servers, but Go is not a language for writing servers. Go is a general purpose programming language, just like C, C++, Java or Python

Really? Even years later in 2025, this never ended up being true. Unless your definition of 'general purpose' specifically excludes anything UI-related, like on desktop, web or mobile, or AI-related.

I know it's written in 2017, but reading it now in 2025 and seeing the author comparing it to Python of all languages in the context of it's supposed 'general purpose'ness is just laughable. Even Flutter doesn't support go. granted, that seems like a very deliberate decision to justify Dart's existence.

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

#35

This is about an explicit argument of type "Context". I'm not a Go user, and at first I thought it was about something else: an implicit context variable that allows you to pass stuff deep down the call stack, without intermediate functions knowing about it. React has "Context", SwiftUI has "@Environment", Emacs LISP has dynamic scope (so I heard). C# has AsyncLocal, Node.JS AsyncLocalStorage. This is one of those id…

> React has "Context", SwiftUI has "@Environment", Emacs LISP has dynamic scope (so I heard). C# has AsyncLocal, Node.JS AsyncLocalStorage.

Emacs Lisp retains dynamic scope, but it's no longer a default for some time now, in line in other Lisps that remain in use. Dynamic scope is one of the greatest features in Lisp language family, and it's sad to see it's missing almost everywhere else - where, as you noted, it's being reinvented, but poorly, because it's not a first-class language feature.

On that note, the most common case of dynamic scope that almost everyone is familiar with, are environment variables. That's what they're for. Since most devs these days are not familiar with the idea of dynamic scope, this leads to a lot of peculiar practices and footguns the industry has around environment variables, that all stem from misunderstanding what they are for.

> This is one of those ideas that at first seem really wrong (isn't it just a global variable in disguise?)

It's not. It's about scoping a value to the call stack. Correctly used, rebinding a value to a dynamic variable should only be visible to the block doing the rebinding, and everything below it on the call stack at runtime.

> Implicit context (properly integrated into the type system) is something I would consider in any new language.

That's the problem I believe is currently unsolved, and possibly unsolvable in the overall programming paradigm we work under. One of the main practical benefits of dynamic scope is that place X can set up some value for place Z down on the call stack, while keeping everything in between X and Z oblivious of this fact. Now, this is trivial in dynamically typed language, but it goes against the principles behind statically-typed languages, which all hate implicit things.

(FWIW, I love types, but I also hate having to be explicit about irrelevant things. Since whether something is relevant or not isn't just a property of code, but also a property of a specific programmer at specific time and place, we're in a bit of a pickle. A shorter name for "stuff that's relevant or not depending on what you're doing at the moment" is cross-cutting concerns, and we still suck at managing them.)

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

#36

This is about an explicit argument of type "Context". I'm not a Go user, and at first I thought it was about something else: an implicit context variable that allows you to pass stuff deep down the call stack, without intermediate functions knowing about it. React has "Context", SwiftUI has "@Environment", Emacs LISP has dynamic scope (so I heard). C# has AsyncLocal, Node.JS AsyncLocalStorage. This is one of those id…

Yeah, I agree 100% with you. The thing with Golang is that it's supposed to be a very explicit language, so passing the context as an argument fits in with the rest of the language.

Nevertheless: just having it, be it implicit or explicit, beats having to implement it yourself.

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

#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.

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

#38
post #27

Earlier quoted context omitted.

Passing the current user ID/tenant ID inside ctx has been super useful for us. We’re already using contexts for cancellation and graceful termination, so our application-layer functions already have them. Makes sense to just reuse them to store user and tenant IDs too (which we pull from access tokens in the transport layer). We have DB sharding, so the DB layer needs to figure out which shard to choose. It does that…

An alternative is to add all dependencies explicitly into function argument list or object fields, instead of using them implicitly from the context, without documentation and static typing. Including logger.

I already talked about it above.

Main problems with passing dependencies in function argument lists:

1) it pollutes the code and makes refactoring harder (a small change in one place must be propagated to all call sites in the dependency tree which recursively accept user ID/tenant ID and similar info)

2) it violates various architectural principles, for example, from the point of view of our business logic, there's no such thing as "tenant ID", it's an implementation detail to more efficiently store data, and if we just rely on function argument lists, then we'd have to litter actual business logic with various infrastructure-specific references to tenant IDs and the like so that the underlying DB layer could figure out what to do.

Sure, it can be solved with constructor-based dependency injection (i.e. request-specific service instances are generated for each request, and we store user ID/tenant ID & friends as object fields of such request-scoped instances), and that's what we had before switching to contexts, but it resulted in excessive allocations and unnecessary memory pressure for our highload services. In complex enterprise code, those dependency trees can be quite large -- and we ended up allocating huge dependency trees for each request. With contexts, we now have a single application-scoped service dependency tree, and request-specific stuff just comes inside contexts.

Both problems can be solved by trying to group and reuse data cleverly, and eventually you'll get back to square one with an implementation which looks similar to ctx.Context but which is not reusable/composable.

>Including logger.

We don't store loggers in ctx, they aren't request-specific, so we just use constructor-based DI.

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

#39

This is about an explicit argument of type "Context". I'm not a Go user, and at first I thought it was about something else: an implicit context variable that allows you to pass stuff deep down the call stack, without intermediate functions knowing about it. React has "Context", SwiftUI has "@Environment", Emacs LISP has dynamic scope (so I heard). C# has AsyncLocal, Node.JS AsyncLocalStorage. This is one of those id…

> React has "Context", SwiftUI has "@Environment", Emacs LISP has dynamic scope (so I heard). C# has AsyncLocal, Node.JS AsyncLocalStorage. Emacs Lisp retains dynamic scope, but it's no longer a default for some time now, in line in other Lisps that remain in use. Dynamic scope is one of the greatest features in Lisp language family, and it's sad to see it's missing almost everywhere else - where, as you noted, it's…

> Emacs Lisp retains dynamic scope, but it's no longer a default for some time now

https://www.gnu.org/software/emacs/manual/html_node/elisp/Va...

> By default, the local bindings that Emacs creates are dynamic bindings. Such a binding has dynamic scope, meaning that any part of the program can potentially access the variable binding. It also has dynamic extent, meaning that the binding lasts only while the binding construct (such as the body of a let form) is being executed.

It’s also not really germane to the GP’s comment, as they’re just talking about dynamic scoping being available, which it will almost certainly always be (because it’s useful).

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

#40

> This probably doesn’t happen often, but it’s prone to name collisions. It's funny, it really was just using strings as keys until quite recently, and obviously there were collisions and there was no way to "protect" a key/value, etc. Now the convention is to use a key with a private type, so no more collisions. The value you get is still untyped and needs to be cast, though. Also there are still many older librarie…

The blog post from 2014 introducing context uses a private key type, so there's really no excuse: https://go.dev/blog/context#package-userip
Post reply on HN