[Date Prev] [Date Next] [Thread Prev] [Thread Next] Date Index Thread Index Search archive:
Date:Mon Jan 27 10:08:43 1993 
Subject:Re: dlocals etc. (and shallow binding) 
From:Aaron Sloman 
Volume-ID:930127.03 

ray@philmtl.philips.ca (Ray Dunn) writes:

> Date: 26 Jan 93 03:39:34 GMT
> Organization: Not Philips.
>
> In refd article, axs@cs.bham.ac.uk (Aaron Sloman) writes:
[AS]
> >A really "clean" implementation would have required all local
> >variables to have their values initialised to some safe default on
> >procedure entry, e.g. to "undef".

[RD]
> ...and to take advantage of internal knowledge of the fact that it doesn't
> raises the hackles of anyone concerned about "future release surprise" or
> portability!

[AS]
But note that this has been a dependable feature of every Pop
implementation for at least 22 years. Suppose it were written into
the manual, would you still object to its use?

In fact, I think that it is an essential part of the dlocal
mechanism that you can access the previous value as well as being
able to update it. E.g. you can do things like

    dlocal hd(list);

        hd(list) + 1 -> hd(list);

in a procedure, with a *guarantee* that on exit from that procedure,
whether normal or abnormal, the previous value stored in the head of
the list will be reset. It would be illogical to disallow the
special case of a value stored in a variable.

Of course a defender of pure functional programming would disallow
the updating of a datastructure in any case, and the argument would
change (which is I think Robin's position, if I've understood it.)

Ray objected to my example use of this facility:

[AS]
> >    define print_procedure( ..... );
> >        dlocal printlevel = printlevel + 1;
>
[RD]
> looks pretty bogus to me.  I've been trying to teach programmers to avoid
> this sort of thing for years.  POP-2 was quite clear about it, even though
> this code would "work".  The reference manual states that when the extent
> of another variable is interrupted "its value is not altered but it cannot
> be accessed or changed by assignment."

[AS]
Well, what I am claiming (if I understand the terminology correctly)
is that allowing the value from the previous context to be accessed,
but not altered, before the first assignment in the new context, is
a very useful extension to the expressive power of the language.

[AS]
>The alternative to these dynamically scoped uses of printlevel and
> >popmatchvars (and similar things, like interrupt, cucharout,
> >prmishap, etc.) would have been to require all these values to be
> >passed explicitly as extra arguments in any procedure that might
> >call a procedure that used these values, ....
> > .....

Which is what Ray recommends:
[RD]
> In 'C' the effect can often be achieved by using a _static_ variable, but
> my own technique is to use a pair of functions - the outer function
> presents a "clean" interface to the outside world and calls a second
> function with all the required extra arguments - it is this second function
> that recurses.
>
> So Aaron's example becomes (ha! I'll do it in POP-2ish)
>
>     vars max_printlevel;
>     5 -> max_printlevel;
>
>     print_procedure(....)
>         aux_print_proc(..... , 1);
>     end
>
>     aux_print_proc(..... , printlevel)
>
>         if printlevel >= max_printlevel then
>             ... print closing bracket and return ....
>         else
>             ... print suitable opening bracket ....
>             aux_print_proc(whatever tail of structure, printlevel+1);
>         close
>     end

Note that this solution works if all printing is done via one
procedure, which used to be the case in POP2, but is no longer the
case in Pop-11, where different data-types have their own
class_print procedures, a bit like an object oriented system where
each class has its own methods (as is the case in Steve Knight's new
objectclass extension to Pop-11) (advertisement).

Extracts from REF PRINT in the online manual follow:

-------------------------------------------------------------------
The basic printing procedure in POPLOG is -sys_syspr-, which prints  any
item in its  standard format. While  this can be  called directly if  so
desired, the  system  additionally  provides  the  procedures  -pr-  and
-syspr- as a  two-stage mechanism  for printing  objects in  a way  that
allows dynamic redefinition of the actual printing procedures used.

    The mechanism  is based  on the  convention that  programs  normally
print objects  using the  variable procedure  -pr-, which  in the  first
place, can be redefined in any  desired way. However, the default  value
of the variable -pr- is the procedure -syspr-, which prints an object by
calling its -class_print- procedure with the object as argument (see REF
*KEYS); thus in the second place, the printing of individual data  types
can be altered by redefining their -class_print- procedures. Because the
default -class_print- of any type is -sys_syspr-, (printing in  standard
format), the normal sequence of events is therefore:

        pr(ITEM)
                ---> syspr(ITEM)
                            ---> class_print(datakey(ITEM))(ITEM)
                                        ---> sys_syspr(ITEM)

    It is important to note, however, that to enable the redefinition of
printing procedures for given  data types to take  effect at any  level,
-sys_syspr- always calls -pr-  to print the  sub-components of any  data
structure (i.e. list,  vector and  record elements,  etc). (Thus  saying
that -sys_syspr-  'prints  any  item  in its  standard  format'  is  not
strictly correct, since  the printing of  sub-components will depend  on
-pr-. A COMPLETELY standard printing procedure would be

        define pr_standard(ITEM)
            lvars ITEM;
            dlocal pr = sys_syspr;
            sys_syspr(ITEM)
        enddefine;

i.e. one that locally redefines -pr- to be -sys_syspr-.)

pr(ITEM)                                            [procedure variable]
        This variable procedure is conventionally used by all procedures
        in the  system  which  print something;  its  default  value  is
        -syspr-.

syspr(ITEM)                                                  [procedure]
        This procedure does

                class_print(datakey(ITEM))(ITEM)

        i.e. apply the -class_print- of the  data type of ITEM to  ITEM.
        The default -class_print- of every data type is -sys_syspr-, but
        this can be redefined as desired -- see REF *KEYS.
-------------------------------------------------------------------

This means that for Ray's mechanism to work, every class_print
procedure would have to take an extra argument, namely the
printlevel, and every other procedure that did any printing would
have to be given a printlevel as part of its input, and then it
would pass on the incremented value to the next printing function it
called. So the above definitions would have to replaced with
something like:

pr(ITEM,INTEGER)
        This variable procedure is conventionally used by all procedures
        in the  system  which  print something;  its  default  value  is
        -syspr-.

syspr(ITEM,INTEGER)
        This procedure does

                class_print(datakey(ITEM))(ITEM,INTEGER)

        unless INTEGER exceeds max_printlevel

I.e. every class_print procedure would need an extra input.

(Incidentally, for printing one normally needs at least two control
variables, one for depth of nesting and one for length of tails,
and Poplog Pop-11 is wrong to have only one variable for nesting,
namely pop_pr_level)

I suspect that Ray's type of solution would be acceptable for
printing, simply because once you get into any printing procedure
you stay within printing procedures which call other printing
procedures explicitly or implicitly (e.g. via class_print), so
there's no real difficulty in passing on the extra parameters.

More generally, however, there are cases where there's a context,
e.g. a database of some kind that's extended by a hypothetical
action, and various different procedures can get called which only
*indirectly*, via several levels of procedure calling eventually
invoke the procedure that further extends the context. For that
situation the extra parameter would have to be supplied to lots of
procedures that do nothing with it except pass it on. If you have
lots of such parameters (cucharout, prmishap, database, interrupt,
etc. etc.) it gets messy. Then Robin's solution of providing a
single environment that's passed on by every procedure looks like
the only attractive solution.

What that amounts to is abandoning global variables that can be
changed locally. It's a consistent way to do things, and maybe has
some nice properties. I suspect that for a large team of programmers
developing a suite of interrelated programs that can call one
another recursively it may be less convenient and less efficient
than using global variables that are dynamically scoped using
dlocal. But I don't know.

[RD]
> Now who has a compiler I can syntax check this with!!

A nice project for a student learning to use Pop-11's compiler
development tools?

Aaron
--
-- 
Aaron Sloman,
School of Computer Science, The University of Birmingham, B15 2TT, England
EMAIL   A.Sloman@cs.bham.ac.uk  OR A.Sloman@bham.ac.uk
Phone: +44-(0)21-414-3711       Fax:   +44-(0)21-414-4281