[REBOL] Re: local vars in functions
From: joel:neely:fedex at: 24-Apr-2002 7:52
Hi, Rishi,
My uninformed $0.02...
(Note that I try to make an alternative suggestion at the
end of my discussion. I'd be interested in feedback on
that idea!)
The main root problem I have with your suggestion is that it
seems to make an assumption about REBOL that I do not believe
to be true -- that there's some sort of conventional "compile"
step that preprocesses the entire text of a piece of code
before generating some internal representation.
For why I think this, see below. Please let me know if I
have misunderstood your meaning...
Rishi Oswal wrote:
...
> What I would like to see is another shortcut to
> creating local variables in any context (function,
> innerfunction, loop). The obvious way I see of doing
> this is as follows:
>
> myfunc: func [][
> localvar:: $25
> myinnerfunc: func [][
> innerlocal:: $10
> print localvar ; prints $25
> ]
> print innerlocal; error!
> ]
> print localvar ; error!
>
Based on your description, should I infer that you would like
the above to be equivalent to?
myfunc: func [/local localvar myinnerfunc] [
localvar: $25
myinnerfunc: func [/local innerlocal] [
innerlocal: $10
print localvar
]
print innerlocal ; error
]
print localvar ; error
Note that I assumed you meant for MYINNERFUNC to be local to
MYFUNC even though you didn't use the double colon (because
of the way you named it).
Do you intend the above meaning or do you intend an
alternative meaning, such as:
myfunc: func [] [
use [localvar] [
localvar: $25
use [myinnerfunc] [
myinnerfunc: func [] [
use [innerlocal] [
innerlocal: $10
print localvar
]
]
print innerlocal ; error
]
]
]
print localvar ; error
Alternately, I could pose it as two questions:
1) What context do you think should contain the double-colon
variables?
1a) The context of the smallest enclosing function
definition?
1b) A new context whose scope is described below?
2) How far should the scope of that context extend (lexically
I mean)?
2a) From just before the double-colon expression to the end
of the enclosing block?
2b) From the beginning of the enclosing block to the end of
the enclosing block?
2c) Just the following expression?
> using the "::" for local var will make it more
> convienient to create local vars (which i use all the
> time over global vars). In addition, it will help
> prevent some errors of accidental global var creation
> because it is now easy to spot a local var.
>
Pardon me, but I must disagree. The typographic differences
among the following expressions
foo: 10
bletch:: "this is a test"
somelongervariablename: foo + length? bletch
shortername:: somelongervariablename + 5
* (thing1: square-root 75) - (thing2:: now/time/second)
lastone: shortername + 7
do *not* IMHO make the local vs. non-local names stand out
all that much.
> Best of
> all, this type of shortcut would not break anything in
> rebol. You could even use this in a loop:
>
> for count 1 10 1 [
> localvar:: "hello"
> ]
>
Based on this example, I assume you *must* mean (1b) is the
context you want, but I'm still unclear what the answer to
(2) would be.
I'm also not clear on what semantics you intend e.g. for the
following case
myfunc1: func [s [string!]] [
a:: 1
foreach char s [
a:: a + 1
if all [#"A" <= char char <= #"Z"] [
a: a + 1
]
if all [#"a" <= char char <= #"z"] [
a:: a + 1
]
if all [#"0" <= char char <= #"9"] [
a: a + 1
]
]
a
]
> Using the "::" shortcut in a global context would be
> the same as using a ":".
>
So "::" behaves differently depending on "context" (I
assume you mean lexical setting, not REBOL "context"...)
and on which time it is being evaluated???
How does it know? (See below.)
That's not the same as defining e.g.
print flarp:: square-root length? "Yowp!"
as meaning the same as
use [flarp] [
print flarp: square-root length? "Yowp!"
]
Remember that LOADing an expression implicitly wraps it in
a block. All of the answers to (2) above have to do with
the idea of the lexically-encl0sing block.
Being able to create, use, and discard "temporary" variables
at the global level is quite handy!
> The disadvantage I see is that it adds another thing
> to the language.. But consider that now we could stop
> using the /local keyword, reduce bugs, and use it
> consistently everywhere, overall it can simplify
> things.
>
But it's *NOT* consistent, IMHO! In the example you give
for count 1 10 1 [
localvar:: "hello"
]
the double-colon-word LOCALVAR does something different the
first time it is evaluated compared to the subsequent time(s)
it is evaluated, although I'm not at all clear what that
something
is.
You also seem to be saying that it must be able to figure out
(dynamically, remember!) which of an arbitrarily large number
of possible contexts should be affected. Remember that REBOL
has no idea of "current context" so in the case (combining your
examples) of
func1: func [n [integer!]] [
func2: func [] [
for count 1 n 1 [
localvar:: count * count
]
]
]
I must again wonder what context LOCALVAR belongs in, and
how the reader (interpreter or human!) should figure that out,
whereas in current REBOL there are four distinct cases, each
of which is clear from the text of the expressions:
;; localvar belongs in the context of func1
func1: func [n [integer!]/local localvar] [
func2: func [] [
for count 1 n 1 [
localvar: count * count
]
]
]
;; localvar belongs in the context of func2
func1: func [n [integer!]] [
func2: func [/local localvar] [
for count 1 n 1 [
localvar: count * count
]
]
]
;; localvar belongs in a context inside the FOR body
func1: func [n [integer!]] [
func2: func [] [
for count 1 n 1 [
use [localvar] [
localvar: count * count
]
]
]
]
;; localvar belongs in some other context established
;; outside this text fragment
func1: func [n [integer!]] [
func2: func [] [
for count 1 n 1 [
localvar: count * count
]
]
]
Because of the way REBOL views words and contexts, I have
real trouble figuring out the interactions of this proposed
new feature with all of the mechanisms already in the
language. There are already enough cases where there are
subtle differences and undocumented behaviors that I really
can't feel good about adding another one.
Finally, in a design situation I always like to ask "What
problem are we trying to solve?" In this case it appears
that the primary problem is the risk of modifying the
global context by forgetting to declare a word as local
to some scope under construction.
If that's the case, I'd like to suggest an alternative.
If I understand correctly, REBOL builds contexts as required
by the expressions being evaluated (e.g., FUNC, USE, FOR,
MAKE OBJECT!, etc.) and refuses to extend those contexts
after initial creation -- with one specific exception being
the global context, which may be extended at any time.
What if REBOL had a new word, e.g. GLOBAL-CONTEXT-FROZEN (and
I'm quite happy for suggestions for a better name!), that
controlled whether adding new words to the global context were
prohibited? Then we could write things like this:
global-context-frozen: true
do %somescript.r
global-context-frozen: false
and any attempt to add a word to the global context during
the evaluation of the script file would cause an error. In
addition, one could "nest" such protection e.g. within a
script file such as somescript.r by writing:
use [temp-global-safe] [
temp-global-safe: global-context-frozen
global-context-frozen: true
;; do a bunch of stuff
;; ...
global-context-frozen: temp-global-safe
]
thus transparently ensuring that the "bunch of stuff" is
evaluated without adding globals, and then restoring the
level of safety that was in place when the above fragment
began.
(Of course, I'm avoiding the issue of whether we would get
benefit from being able to "thaw" other contexts, e.g. to
be able to add attributes to an existing object... ;-)
-jn-
--
; Joel Neely joeldotneelyatfedexdotcom
REBOL [] do [ do func [s] [ foreach [a b] s [prin b] ] sort/skip
do function [s] [t] [ t: "" foreach [a b] s [repend t [b a]] t ] {
| e s m!zauafBpcvekexEohthjJakwLrngohOqrlryRnsctdtiub} 2 ]