Hi all,

Many moons ago I wrote a proposal <https://groups.google.com/g/golang-nuts/c/o3fFHNhiNQs/m/bAyb_5dOCAAJ> to make /execution context/ a fundamental concept in Go, effectively moving `context.Context` to being part of the language, not just an extension.

The main reason to do this is that then it would not be up to the source maintainer to have to do the refactoring it takes to retrofit `ctx context.Context` arguments passed to functions as the first position, as has become the common boilerplate.  In some places, this is impossible: for instance, the io.Reader and related interfaces forbid a `context.Context` argument, so a reader cannot know where it is being read from.

Why does that matter, you might ask?  The main reason is for observability: tracing, logging, debugging, etc.  Of course, context should in general not be used to alter behavior, but for observability, it seems like it would be fair game for the append–only map that `Context` provides, to be available even when working with less than stellar code.

I'm wondering how people would feel about this now?

A summary of my original proposal would be to add a "soft" keyword ("context") which is only meaningful when used before "var", but would not be prohibited from being a variable name.  I did some experimentation with the go parser and found I could get code with 'context' added to is passing the parsing tests, so I'm pretty sure this would work without slowing things down.  That's the syntax I'll use in this message, but of course this is subject to feedback and taste.

Some basic principles stated as text for discussion; there are code examples in the original proposal from August 2017.

 * Semantically, creating a new scope creates a new execution context. 
   Hopefully that is a tautology.
 * Context Variables can be declared in any execution context, using
   `context var` instead of plain `var`, and assuming the `context var`
   is in scope of the observer, to all execution scopes created from
   that execution context, including goroutines.
 * The reverse is not true: the only way you can see changes to a
   variable declared in a high level context, is if the variable has a
   pointer, and the sub–level context changes it (i.e., there's no
   "magic const effect" of declaring a variable with context var.)
 * Functions that don't declare context variables use the same context
   as their caller for retrieving context variables.
     o Declaring it without assigning it in a function allows you to
       read it, without changing the context.  Like other variables
       declared without an assignment, reading it from a context with
       no parent context that has assigned it would reveal the zero value.
     o You can refer to the old value in the rvalue of the declaration,
       or at any point from when you declare it to when you assign it
       within a function.  However, having an assignment in the scope
       should be thought of creating a new context immediately with an
       implicit assignment at the declaration.  I think this behavior
       keeps the semantics as unambiguous as possible, and avoids
       having to do funny stuff when assigning the variable late in a
       function.
 * Scope would work differently.  While the /state/ of the value
   follows the execution context, the visibility of the variable would
   be defined by (a) the package the variable is declared in, and (b)
   the Case of the variable.
     o `context var localInfo` can only be read and written in the
       package it is declared in.
     o While reading the rest of this, be careful not to think of the
       Monty Python Spam song, as this is about Spans, which are not
       the same.
     o `context var OpenSpan Span` declares an exported context
       variable, which can be accessed as below:
     o declaring `context var tracing.OpenSpan` would mean to access
       the `OpenSpan` context variable exported by the `tracing`
       package.  This would only be a compile error if the `tracing`
       package has no `context var OpenSpan x` statement in it.
         + Simply /reading /the context variable from outside the
           package might not need a special syntax:
           `(tracing.OpenSpan)` to read it might work just fine, to
           allow eg:
           `tracing.OpenSpan.Log("happening", tracing.Tag("id", id))`
     o Finally, `context var tracing.OpenSpan =
       tracing.OpenSpan.New("canBeSlowFunc", tracing.InternalKind,
       ...)` is an example of how you could work with the exported
       context variables from a package.  The example there is an
       idiomatic way for a function to declare that it is doing
       something that can be expensive, and so is worthy of tracing
       representation with a new Span.  `Span.New()` would be a
       function that returned a new sub–span of the original Span.

Anyway I think that's the nutshell of it. Thoughts/questions/concerns?

Cheers,
Sam

--
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/24843040-a0f2-4054-8912-acf9defb7697%40vilain.net.

Reply via email to