TLS / 'current values' / any ideas on this


#1

r.e. this library, https://github.com/PistonDevelopers/current

… in the context of the line of thought in this thread,

My question is:- could new compiler or language features be devised that would end the conflict between the “no globals, pass everything from above” programming style, and workarounds like ‘current variables’ (see Philosophy below, it will ramble)

I personally do prefer passing, but the question is: does a newish language make it easier to try out new ideas that might eliminate the conflict around this issue altogether.

Q1 does anything else like this exist ,(other times people direct me at the FP do notation ideas…)

Q2 Would there be a way for the compiler to optimise pattern similar to what is shown in the ‘piston “current” library’ far enough that it’s as efficient as passed parameters

it might require some hints (could that be flagged with attributes?)

what I imagine is the optimiser duplicating function bodies, e.g in pseudocode:-

//'current window' is semantically a thread-local variable, which may be pushed/popped
// similar to dynamic scope

fn my_mod::foo(x){
      ..get_current_window()..  // 'current window' TLS variable accessor function
      do stuff...  
      bar(x)  }```

fn my_mod::bar(y) {
      ..get_current_window()..  // 'current window' TLS variable accessor function
     ..do stuff..
}

// Compiler realises that a group of functions always call 'get_current_window()', 
// so we want to cache that in local, in the hope it will stay in a  register
// the compiler needs the hint that it's return value can only be changed by the 'setters'/push/pop
// that invalidate it.
// so we generate versions of foo, bar() taking the param, and replace their calls..

fn my_mod::foo(x){  foo_with_window(x, get_current_window);}
fn my_mod::bar(y){  bar_with_window(y, get_current_window);}

fn my_mod::foo_with_window(x, z:Window){
    ...do stuff..
    bar_with_window(y, z );
}

fn my_mod::bar_with_window(x,z:Window){
    ..do stuff..
}

this would have to work back through the call-graph, e.g. ‘user code’ outside of ‘my_mod’ could cache ‘get_current_window()’ for a group of calls into ‘my_mod’

fn user_mod::stuff(p) {
    my_mod::foo(p.x)
    my_mod::bar(p.y)
}
//->
fn user_mod::stuff(w){
    let z=my_mod::get_current_window()
    my_mod::foo_with_current_window(p.x, z)
    my_mod::bar_with_current_window(p.y, z)
}

Q2.5 How to figure out how far back through the call graph to go? … does that need an explicit hint?

Q3 does optimisation like this happen already (maybe code trying to cache globals, I realise a compiler may try to keep locals in registers between calls , but this is really about values that are outside the function’s scope)

Q4 how does this relate to an “effects system” (i’ve never seen one, but doesn’t that involve tracing side effects through the call-graph; the motivation for ‘pass from above’ is controlling/communicating side-effects

Q5 Could something like this be done independently of Rust, e.g. as an LLVM -> LLVM transformation, perhaps in a way that would be equally applicable to C++ programs

Philosophy historically my preference has always been ‘pass everything’,‘eliminate globals’, and I’m a big fan of Rust declaring globals ‘unsafe’. but I’ve often worked with people who oppose this idea: I can see the opposing view that the verbosity of repeating a common parameter hurts. ‘jonathan blow’s JAI’ language is interesting to me, but whilst I agree with him on 90% of things, he’s one of the ‘globals-apologists’.

I also revisit this issue r.e. Rusts change in namespacing/overloading approach:

In C++, I take it for granted that the passed system pointers are part of the function name (e.g. if you pass a RenderSystem&, there’s no need to separately put that function in a ‘rendering namespace’ . (This is why I like overloading , it avoids needing to conceptually bias functions to being in a ‘single place’… 'render(Car&, RenderSystem&) is neither in the namespace of 'Car or RenderSystem … it’s effectively tagged with both)

So losing the ‘tool’ of overloading (and losing the fact that the parameter changes the function name), but gaining modules, I ask if the modules (as a point at which information can be specified) can be made to ‘work harder’ on problems like this - hence the idea of macros (or a proposing a language feature…) for ‘shared module-wide parameters’ discussed in that thread.