[Date Prev] [Date Next] [Thread Prev] [Thread Next] Date Index Thread Index Search archive:
Date:Mon May 28 20:25:45 2003 
Subject:Forwarded: Re: Scoped procedures 
From:steve 
Volume-ID:1030528.01 

[This message from Steve Leach was bounced by the central campus spam
filter because it included an upper case occurrence of 'alert!', which
surprises me, given some of the stuff it lets through. Re-sending.
I've turned off rejection of pop-forum mail from non-members because
I was travelling a lot. I am about to go away for a week (Memphis,
ASSC7) and after I return I'll switch on membership monitoring again.
Aaron]


Date: Mon, 26 May 2003 00:35:12 +0100
Hi Dave,

Aaron Sloman's answer is pretty comprehensive so I thought I would offer a
short-and-sweet explanation covering the same ground.

The key point is that in Pop-11 variables are only visible "below" their
declaration.  I have annotated your example to show where the variables a,
b, c and i are visible.

>    define a();            ;;; a
>      lvars i;             ;;; a i
>
>      define b();          ;;; a b i
>        if i < 5 then      ;;; a b i
>          i + 1 -> i;      ;;; a b i
>          c();             ;;; a b i [ALERT! c not declared yet]
>        endif;             ;;; a b i
>      enddefine;           ;;; a b i
>
>      define c();          ;;; a b c i
>        b();               ;;; a b c i
>      enddefine;           ;;; a b c i
>
>      c();                 ;;; a b c i
>    enddefine;             ;;; a b c i

The way to fix the code is by a forward declaration.  The forward declaration
MUST agree with the actual declaration i.e. it should be declared as one of
vars, constant, lvars, or lconstant.  Nested definitions use lconstant as
their default - so that's what you have to use too.

And that's that.

----------------------------------------------------------------------------------

No need to read on, unless you want to understand the
strange world of lconstants ....

I think it is worth adding a fairly lengthy digression about lconstant
declarations here.  lconstant does NOT introduce an 'assign-once lvar' as one
might have imagined from the name.  No, that concept doesn't exist in Pop-11.
(Pity)

The right way to think about it is this.  When you compile an lconstant
declaration, the identifier is bound to an item computed during that
compilation
(i.e. a compile-time constant).  And there are really two quite
distinct uses of
lconstant illustrated below:

      lconstant fred = EXPRESSION;    ;;; execute in special context

      define lconstant bill();  STATEMENTS enddefine;

The first usage requires EXPRESSION to be evaluated in a special
'compile-time'
context.  You can read more about this by going through REF *
VMCODE/sysEXEC_COMPILE,
but it is obvious that limitations arise through trying
to compute things at compile-time.

The second usage, however, has no limitations.  You are even allowed to refer
to 'outer' lexicals!  This is completely unexpected.  Here's an example:

      define outer( x );
          define lconstant inner( _ );
              x
          enddefine;

          inner
      enddefine;

This works because of a quirk of the API of the Poplog code
generation engine, the
Poplog virtual machine.  This API is ultimately responsible for creating new
procedures.  It does this via the following pair:

      sysPROCEDURE( name, num_args )    ;;; call this to start building
                                        ;;; a procedure record

      sysENDPROCEDURE() -> procedure_or_placeholder

The thing that is returned by sysENDPROCEDURE is either the actual
procedure or a
placeholder for the actual procedure; the latter gets produced when
the compiler
thinks things are getting complicated.  However, the virtual machine paddles
furiously under the surface of the water so that you can treat this
placeholder
as if it was the real thing.

Because of this very special treatment of placeholders, all procedure
definitions
are treated as compile-time constants by the virtual machine API!!
And that is
why 'define lconstant' works even when lexical variables are captured etc.

If you have paid close attention so far, you will realize that this means you
cannot write 'outer' (see above) this way, even though you might have
thought it
was a viable alternative:

      define outer( x );
          lconstant inner =
              procedure( _ );
                  x             ;;; sysEXEC_COMPILE doesn't like this
              endprocedure;

          inner
      enddefine;

That is because the expression
      procedure( _ ); x endprocedure
gets evaluated in the special 'compile-time' context implemented by
sysEXEC_COMPILE.
sysEXEC_COMPILE does not see the use of -x- in -inner- as being
properly connected
with the -x- of -outer-.

In the above case you get a relatively sensible reaction, namely
     ;;; DECLARING VARIABLE x
However it is fairly easy to create situations which provoke more
extreme responses.
Here's a good one:

      define aaa(); lvars x;
          lconstant bbb = procedure(); x endprocedure;
          bbb
      enddefine;

      ;;; MISHAP - ILLEGAL CODE FOR COMPILE-TIME EVALUATION
      ;;; DOING    :  define pop_setpop_compiler

Once you understand the basic idea that there is a special
compile-time context for
declarations of this form
      lconstant foo = DO_THIS_IN_SPECIAL_CONTEXT;
you should be OK.


--
Steve