www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - global vs context variable

reply luka8088 <luka8088 owave.net> writes:
Hi everyone!

I would like to address the issue of global variables (or states). In
general my opinion is that they are bad solely because they (in most
cases) lack the ability of alternative values (or states) or ability to
alter them in user friendly way.

For example, take write function from std.stdio. For a third party
function that uses write to output to screen user is unable to redirect
that output to, for example, file without altering third party
function's body. Or if there is a way it rarely user friendly. For
example, in php there are function for buffering output but you have to
manually start buffering and manually stop it. In python active output
is a global variable and you are able to replace it with a custom file
stream but you are responsible maintain a custom stack in case of
recursion and switching it back to default output when no longer needed.
There are also examples of loggers, database connections, etc.

I have a proposal to generalize this issue. Attached is a example
library that implements context based approach and I would like to see
this kind of library in phobos (std.context). It is very simple and yet
in my experience it has shown to be very useful.

Examples using such library:

void writeOutput () {
  writeln("example output");
}

void main () {

  writeOutput();

  standardOutputContext(file("example.txt"), {
    writeOutput();
  });

}

MVC example:

void databaseToView () {
  auto result = db.query("select;");
  view.populate(result);
}

void myAction () {

  auto customView = new View();

  viewContext(customView, {
    databaseToView();
  });

  view.regionA.append(customView);

}

void main () {

  dbContext(defaultDbConnection {
    viewContext(defaultView, {
      myAction();
    });
  });

}


I would like to add this to phobos and document it but I would like to
know if this is desirable at all and to get a community feedback.

Thoughts?
Dec 11 2013
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 11 December 2013 at 08:21:33 UTC, luka8088 wrote:
 Hi everyone!

 I would like to address the issue of global variables (or 
 states). In
 general my opinion is that they are bad solely because they (in 
 most
 cases) lack the ability of alternative values (or states) or 
 ability to
 alter them in user friendly way.

 For example, take write function from std.stdio. For a third 
 party
 function that uses write to output to screen user is unable to 
 redirect
 that output to, for example, file without altering third party
 function's body.
"write" is really just a global helper/shortcut function that calls "stdout.write". If the user wants to customize this, then it's really no more complicated than doing a write to a named stream, which represents the global out, which may or may not be stdout: auto myGlobalOut = stdout; myGlobalOut.write(); //Writes to console (technically, "standard output") //Change the global output context myGlobalOut = File("out.txt", "w"); myGlobalOut.write(); //Writes to file -------- I'm not really sure I understood the rest of what you posted though, so I can't make any comments on that.
Dec 11 2013
parent luka8088 <luka8088 owave.net> writes:
On 11.12.2013. 9:30, monarch_dodra wrote:
 
 "write" is really just a global helper/shortcut function that calls
 "stdout.write". If the user wants to customize this, then it's really no
 more complicated than doing a write to a named stream, which represents
 the global out, which may or may not be stdout:
 
 auto myGlobalOut = stdout;
 myGlobalOut.write(); //Writes to console (technically, "standard output")
 
 //Change the global output context
 myGlobalOut = File("out.txt", "w");
 myGlobalOut.write(); //Writes to file
Yes. That is exactly what this kind of approach is all about. In practice I found a need to temporarily change myGlobalOut while keeping in mind that recursion could be used and in most cases a stack was required. You also need to make sure that stack is pop'ed properly (even in case of throwing) so generally there is some work to be done. I think that this kind of approach would be much more user friendly if a library support was introduced.
 
 --------
 
 I'm not really sure I understood the rest of what you posted though, so
 I can't make any comments on that.
Take MVC for example. Most frameworks either support global variables or setting a controller properties for stuff that needs to be accessible globally. I have seen database connections being a property of a controller, which if you think about it makes no sense, but it was the most pragmatical way to do it in order to make database connection "globally" accessible without setting is as a global variable.
Dec 11 2013
prev sibling next sibling parent reply "QAston" <qaston gmail.com> writes:
On Wednesday, 11 December 2013 at 08:21:33 UTC, luka8088 wrote:
 Hi everyone!

 I would like to address the issue of global variables (or 
 states). In
 general my opinion is that they are bad solely because they (in 
 most
 cases) lack the ability of alternative values (or states) or 
 ability to
 alter them in user friendly way.

 For example, take write function from std.stdio. For a third 
 party
 function that uses write to output to screen user is unable to 
 redirect
 that output to, for example, file without altering third party
 function's body. Or if there is a way it rarely user friendly. 
 For
 example, in php there are function for buffering output but you 
 have to
 manually start buffering and manually stop it. In python active 
 output
 is a global variable and you are able to replace it with a 
 custom file
 stream but you are responsible maintain a custom stack in case 
 of
 recursion and switching it back to default output when no 
 longer needed.
 There are also examples of loggers, database connections, etc.

 I have a proposal to generalize this issue. Attached is a 
 example
 library that implements context based approach and I would like 
 to see
 this kind of library in phobos (std.context). It is very simple 
 and yet
 in my experience it has shown to be very useful.

 Examples using such library:

 void writeOutput () {
   writeln("example output");
 }

 void main () {

   writeOutput();

   standardOutputContext(file("example.txt"), {
     writeOutput();
   });

 }

 MVC example:

 void databaseToView () {
   auto result = db.query("select;");
   view.populate(result);
 }

 void myAction () {

   auto customView = new View();

   viewContext(customView, {
     databaseToView();
   });

   view.regionA.append(customView);

 }

 void main () {

   dbContext(defaultDbConnection {
     viewContext(defaultView, {
       myAction();
     });
   });

 }


 I would like to add this to phobos and document it but I would 
 like to
 know if this is desirable at all and to get a community 
 feedback.

 Thoughts?
This issue is probably nearly as old as programming itself. There are several sollutions already developed: dependency injection (requires complex configuration rules), manually passing deps as args (cumbersome), service locator or global variables. Your sollution as far as I understand it relies on swapping a global variable when inside a context and restoring it afterwards. While this would be perfectly fine in environment where code is executed in OS threads, there will be a problem in the case of Vibe.d which uses fibers. But I guess the problem is solvable there aswell. Just to note - you have to bind to globals at some point - what if someone wants to swap writeln function whith his own (for example to call logger, or for whatever reason)? Or he may want to swap dbContext :).In my practice I make swappable only things I think I'll need to swap in future.
Dec 11 2013
parent reply luka8088 <luka8088 owave.net> writes:
On 11.12.2013. 10:53, QAston wrote:
 
 This issue is probably nearly as old as programming itself.
 There are several sollutions already developed: dependency injection
 (requires complex configuration rules), manually passing deps as args
 (cumbersome), service locator or global variables.
Yeah, and it always keeps pooping right up!
 
 Your sollution as far as I understand it relies on swapping a global
 variable when inside a context and restoring it afterwards. While this
 would be perfectly fine in environment where code is executed in OS
 threads, there will be a problem in the case of Vibe.d which uses
 fibers. But I guess the problem is solvable there aswell.
I was thinking about such issues and think they are all solvable. I concluded that I should present this issues as simple and possible to check out the general interest first.
 
 Just to note - you have to bind to globals at some point - what if
 someone wants to swap writeln function whith his own (for example to
 call logger, or for whatever reason)? Or he may want to swap dbContext
 :).In my practice I make swappable only things I think I'll need to swap
 in future.
Yes. Not only that swapping would need to permitted but also only same type values could be swapped. D makes sure of that.
Dec 11 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 11 December 2013 at 12:58:54 UTC, luka8088 wrote:
 On 11.12.2013. 10:53, QAston wrote:
 
 This issue is probably nearly as old as programming itself.
 There are several sollutions already developed: dependency 
 injection
 (requires complex configuration rules), manually passing deps 
 as args
 (cumbersome), service locator or global variables.
Yeah, and it always keeps pooping right up!
I hate it when issues just keep pooping up. So rude!
Dec 11 2013
parent reply luka8088 <luka8088 owave.net> writes:
On 11.12.2013. 15:47, monarch_dodra wrote:
 On Wednesday, 11 December 2013 at 12:58:54 UTC, luka8088 wrote:
 Yeah, and it always keeps pooping right up!
I hate it when issues just keep pooping up. So rude!
Lol, popping xD
Dec 11 2013
parent Ary Borenszweig <ary esperanto.org.ar> writes:
On 12/11/13 12:54 PM, luka8088 wrote:
 On 11.12.2013. 15:47, monarch_dodra wrote:
 On Wednesday, 11 December 2013 at 12:58:54 UTC, luka8088 wrote:
 Yeah, and it always keeps pooping right up!
I hate it when issues just keep pooping up. So rude!
Lol, popping xD
shit happens
Dec 11 2013
prev sibling next sibling parent reply Shammah Chancellor <anonymous coward.com> writes:
On 2013-12-11 08:21:35 +0000, luka8088 said:

 Examples using such library:
 
 void writeOutput () {
   writeln("example output");
 }
 
 void main () {
 
   writeOutput();
 
   standardOutputContext(file("example.txt"), {
     writeOutput();
   });
 
 }
What does this method have over just using: with(file("example.txt")) { writeln("Foo"); }
Dec 11 2013
parent luka8088 <luka8088 owave.net> writes:
On 11.12.2013. 12:14, Shammah Chancellor wrote:
 On 2013-12-11 08:21:35 +0000, luka8088 said:
 
 Examples using such library:

 void writeOutput () {
   writeln("example output");
 }

 void main () {

   writeOutput();

   standardOutputContext(file("example.txt"), {
     writeOutput();
   });

 }
What does this method have over just using: with(file("example.txt")) { writeln("Foo"); }
It works with deep nesting without the need to pass the context as a function argument: void writeOutput () { writeln("example output"); } void f2 () { writeOutput(); } void f1 () { f2(); } void main () { f1(); standardOutputContext(file("example.txt"), { f1(); }); }
Dec 11 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 12/11/13 09:21, luka8088 wrote:
 Hi everyone!
 
 I would like to address the issue of global variables (or states). In
 general my opinion is that they are bad solely because they (in most
 cases) lack the ability of alternative values (or states) or ability to
 alter them in user friendly way.
 
 For example, take write function from std.stdio. For a third party
[...]
 void writeOutput () {
   writeln("example output");
 }
 
 void main () {
   writeOutput();
 
   standardOutputContext(file("example.txt"), {
     writeOutput();
   });
 }
import std.stdio; void writeOutput () { writeln("example output"); } void main () { writeOutput(); { auto ex = Push!stdout(File("example.txt", "w")); writeOutput(); } writeOutput(); } struct Push(alias A, T=typeof(A)) { T old; this(T a) { old = A; A = a; } ~this() { A = old; } } Last time I checked, 'with' was still broken (the object is destroyed too soon), but once that is fixed you should be able to do: with (Push!stdout(File("example.txt", "w"))) writeOutput(); artur
Dec 11 2013