[REBOL] context of a function Re:(6)
From: brian:hawley:bigfoot at: 23-Aug-2000 13:21
Galt Barber <[galtbarber--MailAndNews--com]> wrote:
>Brian, /local revelation! Thanks!
>
>So, you really can see that when invoked,
>/local is normally set to none, and
>all "optional switch params" following it,
>up to the next /switch if any, are set to none also.
>
>Somehow I never realized that MULTIPLE parameters
>can follow a /switch in the function definition
>so I thought that /local was doing something special
>allowing multiples, and that it had to come last.
Pretty cool, huh?
>So, you could use any [parma parmb /local x /morefrickinlocals s t u]
>and s t u are locals that are just every bit as good as /local,
>and it doesn't even matter if /local is the last switch, right?
>The only limit is that all real params have to come
>before the first /switch.
All of this only matters to the help function. Any
parameters or switches to your function that are
after the /local switch are left undocumented by the
help function. If you want undocumented switches,
this is the way to do it.
Keep in mind that the source function shows all of
your parameters and code, so "undocumented" is a
little relative here.
>What did this save RT? Did they get to avoid having
>to use 'use inside the function to create a separate
>context frame with the locals local? Since they
>had to support the /switches anyway and they had
>a mechanism for that, why not bag the extra 'use
>and stick the locals as "unused" switch parms?
>was their thinking, I suppose.
As I recall (anybody correct me here), there didn't
used to be any special treatment of /local at all.
All of the formal parameters, both refinements and
words, can be used as local variables.
I guess that people didn't realize this so they asked
for local variables. Some genius at RT (there are a
lot of them) figured out that undocumented local vars
would do as well, and hacked the help function.
As for functions' use of contexts, all contexts are
the same underneath. The only difference is in how
the contexts are treated underneath and the lifespan.
By lifespan, I mean during what part of the execution
process the words in that context are valid. Any time
outside of that lifespan, it's best to program as if
that context no longer exists and you could crash the
interpreter if you refer to it (mostly true).
Here's a quick comparison of the different contexts.
Words in a context are not initially set to a value,
but some users of contexts do some initialization.
Of course all of this is subject to change later by
RT, particularly when modules are introduced.
Global (system/words):
- Created: When REBOL starts?
- Lifespan: Duration of interpreter session.
- Strangeness: Only context that words can be added to
after it has been created. Otherwise, like Object.
Use function:
- Created: When the function use is called.
- Lifespan: For the duration of that function call.
Object! :
- Created: When the object! is created.
- Lifespan: For as long as the object! is referenced.
- Initialization: Code block initializes object words
at creation time.
- Strangeness: Always has self word, can be based on
prototype of other object (_not_ inheritance).
Function! :
- Created: Conceptually at start of function call. For
real behavior, see Strangeness.
- Lifespan: Conceptually until end of function call.
For real behavior, see Strangeness.
- Initialization: Formal parameters (the words of the
context) are set to either the values of the actual
parameters, or to none! if none. Missing parameters
at the end are left unset.
- Strangeness: Conceptually the context of a function!
should be treated as if it had the duration of a use
context - you shouldn't try to treat the words of
the function! as variables after the function is done
executing, unless you bind them to a valid context.
In practice REBOL caches a single context for each
function to cut down on context creation and rebinding
overhead, making REBOL _much_ faster. However, this
context is reused with every function call and treated
as a stack frame during recursive calls, making use of
the context outside of the function generally a bad
idea. It is a particularly bad idea for those of us
who are used to the languages Lisp or Scheme because
REBOL doesn't close those variables (a Lisp term). The
actual behavior of REBOL will be close enough to Scheme
to make it very difficult to see where the bugs are,
and won't generate a helpful error message to tell you
what you did wrong.
Because of these caveats, it's best to translate your
closure-based code into objects until you know what to
avoid doing. Any code that makes you wish that the IN
function was defined for functions should be rewritten,
probably to use objects or modules instead. When you
really need closures, close the values yourself with
compose or something. If you need static data, use a
literal series embedded in the source code. Above all,
treat function! contexts as if they are only valid as
long as use contexts.
>The one change they had to make to accept this is
>that /local unused switch params (local vars) are
>set to none like any other unused switch parms
>(since /local itself is not usually invoked).
They didn't need to make any changes to the creation or
evaluation of functions at all. Functions are simpler
than most think they are. All they changed was help.
>This is kind of amusing:
>
> >> g: func [][probe local ]
> >> g
>** Script Error: local has no value.
>** Where: probe local
>
> >> g: function [][][probe local ]
> >> g
>none
>== none
> >>
Yup! :)
Brian Hawley