local variable appears in workspace
A Logo programming environment for Microsoft Windows
Brought to you by:
david_costanzo
I have a problem which I cannot explain. In a procedure (one of many) I declared a local variable 'choice'. Later, when I wrote SHOW ITEM 2 CONTENTS, the variable 'choice' appeared in the workspace as global. I checked the whole bunch of procedures, but there was no other variable 'choice' which could be global.
Now I wonder what the reason may be that the variable, which is declared local, appears as global afterwards.
Here is the procedure:
TO lh.go.button.inspect_for_experts
;---------:---------:---------:---------:---------:---------:-----
; Button Event Handler - Inspect Read Only
;---------:---------:---------:---------:---------:---------:-----
; Prepare variables
LOCAL [choice result n note]
; Apply the selectbox to find the option number (0 for CANCEL).
CATCH "ERROR [
MAKE "choice
SELECTBOX [What do you want to inspect?] [
|standby|
|procedures in workspace|
|global variables in workspace|
|property lists in workspace|
|buried procedures in subspace|
|buried global variables in subspace|
|buried property lists in subspace|
]
]
; If an error appeared due to CANCEL or X, just end the search.
IF NOT EQUAL? ERROR [] [STOP]
; Delete the contents of the console
CLEARTEXT
; Treat choice 2 (procedures in workspace)
IF EQUAL? :choice 2 [
MAKE "result ITEM 1 CONTENTS
MAKE "n COUNT :result
PRINT [>>> List of procedures in workspace:]
IFELSE EQUAL? :n 0 [
MAKE "note [No procedures in workspace.\n\n]
][
MAKE "note (SENTENCE
[Procedures in workspace:] :n [\n\n]
[Click and see list in console below.\n]
)
]
SHOW :result
lh.go.inform :note
]
; Treat choice 3 (global variables in workspace)
IF EQUAL? :choice 3 [
MAKE "result ITEM 2 CONTENTS
MAKE "n COUNT :result
PRINT [>>> List of global variables in workspace:]
IFELSE EQUAL? :n 0 [
MAKE "note [No global variables in workspace.\n\n]
][
MAKE "note (SENTENCE
[Global variables in workspace:] :n [\n\n]
[Click and see list in console below.\n]
)
]
SHOW :result
lh.go.inform :note
]
; Treat choice 4 (property lists in workspace)
IF EQUAL? :choice 4 [
MAKE "result ITEM 3 CONTENTS
MAKE "n COUNT :result
PRINT [>>> List of property lists in workspace:]
IFELSE EQUAL? :n 0 [
MAKE "note [No property lists in workspace.\n\n]
][
MAKE "note (SENTENCE
[Property lists in workspace:] :n [\n\n]
[Click and see list in console below.\n]
)
]
SHOW :result
lh.go.inform :note
]
; Treat choice 5 (procedures in subspace)
IF EQUAL? :choice 5 [
MAKE "result ITEM 1 BURIED
MAKE "n COUNT :result
PRINT [>>> List of buried procedures:]
IFELSE EQUAL? :n 0 [
MAKE "note [No procedures buried.\n\n]
][
MAKE "note (SENTENCE
[Procedures in subspace:] :n [\n\n]
[Click and see list in console below.\n]
)
]
SHOW :result
lh.go.inform :note
]
; Treat choice 6 (global variables in subspace)
IF EQUAL? :choice 6 [
MAKE "result ITEM 2 BURIED
MAKE "n COUNT :result
PRINT [>>> List of buried global variables:]
IFELSE EQUAL? :n 0 [
MAKE "note [No global variables buried.\n\n]
][
MAKE "note (SENTENCE
[Global variables in subspace:] :n [\n\n]
[Click and see list in console below.\n]
)
]
SHOW :result
lh.go.inform :note
]
; Treat choice 7 (property lists in subspace)
IF EQUAL? :choice 7 [
MAKE "result ITEM 3 BURIED
MAKE "n COUNT :result
PRINT [>>> List of buried property lists:]
IFELSE EQUAL? :n 0 [
MAKE "note [No property lists buried.\n\n]
][
MAKE "note (SENTENCE
[Property lists in subspace:] :n [\n\n]
[Click and see list in console below.\n]
)
]
SHOW :result
lh.go.inform :note
]
END
Meanwhile I've got an idea of which could be the reason.
I get a list of all global variables by the line
Now everything depends on where this line is embedded. As an example I'd take
When I now write in the commandline
the list of global variables appears twice - the first result includes variable 'look_at_me' in the list, the second result doesn't.
So I think that all local variables of the procedure which includes the line
are treated as if they were global, despite they're declared as local, because they are still active.
This should solve the problem
Well, I did not propose any alternative - but here it is:
IF I now call do_something, it renders the list of global variables finally, but 'look_at_me' is not in it.
One more step makes it stell far simpler - at least in this case
When the LOCAL statement implies that the variable 'look_at_me' gets lost as soon as the procedure closes, then I can replace LOCAL [look_at_me] by a global variable which is erased at the end of the procedure. And if SHOW ITEM 2 CONTENTS comes after 'look_at_me' has been erased, it is not influenced by it. This can be done for even more pseudo-local variables.
Finally, I have a question.
If I write a procedure
and call it, there will be in the console the name Anatol and in the list of global variables anything but NOT the variabloe 'look_at_me.
That means, I can let a local variable vanish still before the procedure, to which it belongs, ends, by using ERASE. I always thought that this is only possible for global variables, because the FMSLogo-documentation says "ERASE Erases the procedures, variables, and property lists named in contentslist.
Is it therefore safe to apply ERASE alo to local variables?
I never considered the interaction between ERASE and dynamic scoping, but, yes, ERASE does do what you want. I'll document the current behavior and add tests so that it's safe to rely on it.
You may be interested why it works on a technical level. The "workspace" subsystem is completely separate from the "dynamic scoping" system. As far as the CONTENTS, MAKE, and the rest of the workspace is concerned, all variables are global variables. It's the evaluator that implements dynamic scoping, storing the shadowed value of the global variable on what amounts to a call stack when a new local variable is created and then restoring the old value when the defining procedure goes out of scope. As far as the workspace is concerned, there's only one variable and it's value is just being changed.
ERASE works by setting the variable value to "Unbound", which is what FMSLogo uses for variables with no values. This tells CONTENTS to skip it. When the current procedure goes out of scope, the shadowed value is restored, creating the illusion of re-creating the variable.
This separation of workspace and dynamic scoping also explains why there's no workspace predicate to determine if a variable is local. The workspace subsystem doesn't have that information; it's hidden on the evaluator's call stack.
Another implication is worth noting. If a global variable named "choice" were shadowed by your local variable, you wouldn't see it the editor because you just erased it. And if someone saved an editor that has
Make "choice 1whilelh.go.button.inspect_for_expertsis running, it would temporarily re-create variable. Then whenlh.go.button.inspect_for_expertsexits, that value would disappear. In fact, there is no way for the workspace procedures to access a shadowed variable value.Another implication is that if someone has a global variable named "choice", opened the editor, and happened to change it while
lh.go.button.inspect_for_expertswas running , they'd overwrite your local variable, not their global variable. If this sounds bad to you, you may consider renaming your local variables to something like ".lh.choice" to reduce the risk of a naming conflict with the programmer's variables.Bug #65 and Feature Request #74 are about local variables appearing in the editor.
Related
Bugs: #65
Feature Requests: #74
The explanations you sent me in March were really interesting and helpful for us. But while I was struggling for the economic survival of pbreport, I found no extra time to answer it. Now everything is fixed, and we're going to complete the things we created around Little Helper.
First, let me thank you especially for drawing my attention to what I'd like to call the 'choice'-conflict. This problem arises only when checking variables, but neither for procedures nor for properties. But I can't escape, and therefore I'll give the inner variable 'choice' a very cryptic name No programmer would see it from outside.
In Germany a schoolyear goes from summer to summer, and during the current schoolyear we have a special group of 15 years old boys (no girls) learning informatics using FMSLogo and the addon we made out of Little Helper. We told the students that FMSLogo and this addon are seperate things working together without altering each other, and we named the addon for them KISS.lgo, as if it where a little framework aside of FMSLogo. This worked well, and so we can teach FMSLogo and the addon separately. There are the chapters our tutorial which we are testing, and we had to promise that the tutorial together with a booklet for teachers will be available for the next schoolyear beginning in September. Then we should have three new groups instead of one, also with girls, as we hope. So there are only three months left to get it all ready.
In the group of the current schoolyear there are also migrants struggling with the German language. We decided to keep all the technical vocabulary in English without translating it, but we did explain things (and write the tutorial) also in German. The tutorial(s) and the booklet for teachers sooner of later will be published in German, English, and Chinese, but neither the English programming language itself nor its help system will be translated. We expect the students to learn the English vocabulary together with the programming language. It's easier than always inventing funny new words.
As Little Helper and KISS.lgo changed a lot during the last months as a consequence of teaching it, we could not complete a final version. We have to finish it until August this year - this is a deadline we cannot influence. But as soon as it is stable so far, I'd like to send it all to you first - and we could consider what to do with it.
We decided not to change anything in FMSLogo, not even the EDALL button. But the addon really gives us many new opportunities, especially relative paths througout. Very important is also that we based the storage of program material on 'collections of procedures' instead on the unburied workspace as a whole (which generates too much complexity for someone who just wants to save his or her work).
Nevertheless, you can even switch KISS.lgo off while FMSLogo is running, and do everything you are used to do with FMSLogo. When you decide to get KISS.lgo back, nothing - whether buried or not - is lost from its data while it is off (and even erased). As long as new versions of FMSLogo stay downwards compatible, any version of KISS.lgo will do.
It should be mentioned that KISS.lgo is nearly completely written in FMSLogo itself, just like a bootstrap-expansion of FMSLogo would be. Regrettably, I guess that this might not be the most efficient way.
As soon as our work is done, we'll send it to you before it is published.