[comp.lang.misc] Request For Comment About Handling

stt@inmet.inmet.com (11/17/90)

Re: Use of globals in a new programming language

I would recommend that you provide the concept
of "context" variables rather than "global" variables.
What this means is that each procedure or function
which wants access to the global environment must
receive an additional implicit "context" parameter.  

By default, a procedure/function which receives
a context parameter passes it down to every procedure/function
it calls which requires a context parameter.
However, it may establish a new context with different
values for some of the variables, but this change only
affects the nested calls, and has no effect outside.

The only way to have an effect outside is if the
context variable is actually a pointer to something.
This "thing" pointed to may be altered, but such
alterations would generally require mutual exclusion,
presuming the thing might be pointed to from multiple
threads of control.

A totally "pure" procedure/function would receive no
context parameter (a so-called "context-free" procedure/function).
Such a procedure/function could only depend on its parameters,
and only affect its parameters.  A pure function given the
same parameters would always return the same result.

A "side-effect-free" procedure/function would receive
the context parameter in a mode which would disallow
updating things pointed-to by the context.  Such a procedure/function 
could only affect its parameters/produce a result, 
though its affect might depend on the state of the context.

A "normal" procedure/function would receive the context
parameter in a mode allowing it to update things
pointed to by the context.

Note that this concept of "context" matches nearly
exactly that which is represented by the environment
variables of Unix, or the logical symbols of VMS.
It also corresponds pretty closely to the "special"
variables of Common Lisp, which are dynamically rather
than lexically scoped.  An important feature, however,
is that in a multi-threaded language, each thread
could have a distinct context.

The context could either be a flat name space, or
more usefully, an hierarchical name space.

If you have declarations in your language, then
context "variables" would be declared at the outermost
level, but this declaration would only specify the
type and default initial value.  The current value
would depend on the innermost caller which established
a new value for it.

A major advantage of the "context" approach to globals
is that it scales up nicely to multi-threaded and even
multi-user environments.  The term "global" is misleading
since we know it really only means program-wide, not globe-wide.
However, in a highly interactive, multi-threaded, multi-user
environment, you really might want to have objects shared
across threads and/or programs.  To do so using contexts,
you would create an object in a shared memory area (e.g.
the filesystem), and then point to it from one or more
contexts.

Contexts match very nicely concepts like "current directory"
or "standard output" present in most operating systems.
They also work well for things like a seed for a random
number package, where you might want each thread in a multi-threaded
application to have its own seed, allowing a repeatable random-number
sequence, independent of the relative rates of execution of the
various threads.  

In fact, in my experience, most uses for globals are more
safely and flexibly handled by the concept of context.

S. Tucker Taft       stt@inmet.inmet.com
Intermetrics, Inc.
Cambridge, MA  02138

rh@smds.UUCP (Richard Harter) (11/18/90)

In article <21900002@inmet>, stt@inmet.inmet.com writes:


: I would recommend that you provide the concept
: of "context" variables rather than "global" variables.
: What this means is that each procedure or function
: which wants access to the global environment must
: receive an additional implicit "context" parameter.  

The key distinction being, I gather, that a procedure is
told where it getting externals from rather than declaring
where it gets externals from.  Are you really thinking in
terms of a single "context" parameter?

: By default, a procedure/function which receives
: a context parameter passes it down to every procedure/function
: it calls which requires a context parameter.
: However, it may establish a new context with different
: values for some of the variables, but this change only
: affects the nested calls, and has no effect outside.

This sounds a lot like traditional nested scope.  If I am
following you correctly I see some problems.  Quite frequently
one wants to put the context definition code one or more
layers down from the top level entry point, e.g.

	procedure do-good-stuff
		....
		establish-local-context
		....
		do-the-dirty-work

If the top level routine has to have all of the context definition
code it becomes overburdened with a large declaration section.

: The only way to have an effect outside is if the
: context variable is actually a pointer to something.
: This "thing" pointed to may be altered, but such
: alterations would generally require mutual exclusion,
: presuming the thing might be pointed to from multiple
: threads of control.

: A totally "pure" procedure/function would receive no
: context parameter (a so-called "context-free" procedure/function).
: Such a procedure/function could only depend on its parameters,
: and only affect its parameters.  A pure function given the
: same parameters would always return the same result.

It is part of the language design that all functions are "pure".

: A "side-effect-free" procedure/function would receive
: the context parameter in a mode which would disallow
: updating things pointed-to by the context.  Such a procedure/function 
: could only affect its parameters/produce a result, 
: though its affect might depend on the state of the context.

: A "normal" procedure/function would receive the context
: parameter in a mode allowing it to update things
: pointed to by the context.

This raises the question of who decides whether a procedure
is "side-effect-free" or "normal", the calling procedure or the
called procedure.  My view is that an update within a procedure
should always succeed with the proviso that, in a "side-effect-free"
mode the update is made to a copy.

One issue is whether one decides to make "side-effect-free" an integral
language concept (the default being to let the chips fall where they
may.)  If one does, is the attribute of side-effect-freeness inherited?
If so, what does one do about service routines which might have side
effects?

: Note that this concept of "context" matches nearly
: exactly that which is represented by the environment
: variables of Unix, or the logical symbols of VMS.

This is a pretty dubious recommendation. :-)

: It also corresponds pretty closely to the "special"
: variables of Common Lisp, which are dynamically rather
: than lexically scoped.  An important feature, however,
: is that in a multi-threaded language, each thread
: could have a distinct context.

The language is designed to make multi-threading easy,
so this is an important point.

: The context could either be a flat name space, or
: more usefully, an hierarchical name space.

My experience is that flat name spaces suck (you should
excuse the language.)  One always runs into name-space
pollution problems.  Actually, at the procedure level,
the name space is flat.  This is, in my view, the right thing
to do.  Procedures are small and you can afford a flat name
space.  The route that I am following is that all names are
local except those imported in by mechanisms currently under
study.

: If you have declarations in your language, then
: context "variables" would be declared at the outermost
: level, but this declaration would only specify the
: type and default initial value.  The current value
: would depend on the innermost caller which established
: a new value for it.

There are no type declarations.  The language has a single
type; each variable has its value a list of strings.  There is
provision for scope declarations.  Currently a variable can
be declared local or global.  This is admittedly primitive
but works fine for small applications.  Call it an interim
solution while figuring out how to do it right.

: A major advantage of the "context" approach to globals
: is that it scales up nicely to multi-threaded and even
: multi-user environments.  The term "global" is misleading
: since we know it really only means program-wide, not globe-wide.
: However, in a highly interactive, multi-threaded, multi-user
: environment, you really might want to have objects shared
: across threads and/or programs.  To do so using contexts,
: you would create an object in a shared memory area (e.g.
: the filesystem), and then point to it from one or more
: contexts.

Just the kind of thing that I am thinking about.  I have to
admit, however, that I don't quite see how the approach that
you are suggesting works in an object-oriented context.
[This isn't an OOL, but one certainly wants to use OOL techniques.]
Objects and their methods have a private context which is
shared across a set of procedures (methods, whatever) which
is not inherited from the calling environment.  A shared memory
area sounds a global to me (restricted access globals are
still globals).

: Contexts match very nicely concepts like "current directory"
: or "standard output" present in most operating systems...

I like that term, "current dictionary".

: In fact, in my experience, most uses for globals are more
: safely and flexibly handled by the concept of context.

I agree.  My thinking has run along those lines.  Perhaps I
have misjudged the matter but I see the need for multiple
contexts which could make things very messy unless one is
very very careful.  

Thank you for the comments.
-- 
Richard Harter, Software Maintenance and Development Systems, Inc.
Net address: jjmhome!smds!rh Phone: 508-369-7398 
US Mail: SMDS Inc., PO Box 555, Concord MA 01742
This sentence no verb.  This sentence short.  This signature done.