Dimitrii Pasechnik asked:
> I don't understand why the following does not work. > > gap> tt:=function(infile) > > local A; > > Read(infile); > > return A; > > end; [...]
This problem has been discussed in the forum several times before, for your
convenience I append an answer of Martin Sch"onert to basically the same
(By the way: The full GAP distribution contains in the 'etc' directory files
'forum9xy.txt which archive older forum mails, you can also browse them on
our 3W pages.)
PS. This happened when I tried to Exec something producing a
GAP-readable file and then Read it there, all within a function.
What would be the alternative to what I'm trying to do?
Make 'A' a global variable.
============== old message ================================ >From firstname.lastname@example.org Mon Nov 2 21:58:52 1992 Date: Mon, 02 Nov 1992 21:58:52 +0100 From: Martin Schoenert <email@example.com> Subject: Re: Reading from files
Eamonn O'Brien writes in his e-mail message of 2-Nov-92
Why does the following piece of code not work? It generates
an error message
Error, Variable 'G' must have a value at
I know that by not declaring G locally within the procedure
I can persuade it to work OK -- but if I do that, it
overwrites whatever existing value G had.
This may have serious side effects -- where for example
I have a group G defined in Gap, and I now want to read
another group called G defined in "file.g" and return it
from ReadFromFile to the main level
under some other name. Is there any sensible way to
achieve this without wiping out my existing G?
I guess that I can encase the definition of G in the file
within a function and call that function. But are there
other approaches? Is this problem something which is
intended /unavoidable / or a bug?
firstname.lastname@example.org################################################## #code to read value from file and return it ReadFromFile := function ()
local exist, G;exist := READ("file.g"); return G; end; #ReadFromFile
l := ReadFromFile ();
Print ("The value returned from the function is ", l, "\n");############################################################ #contents of file.g G := 5;
Let me try to explain why this works this way. I make this fairly long
because most descriptions of programming languages are quite brief in
this regard (in fact most fail to describe the difference between
identifiers and variables).
On the one hand there are *variables*. A variable is an abstract entity.
Variables are either global variables, formal argument variables of
functions, or local variables of functions. Global variables are created
on demand. With each function call GAP creates one variable for each
formal argument (i.e., for each identifier that appears in the
parenthesis following the 'function' keyword in the function's
definition), and one variable for each local (i.e., for each identifier
that follows the 'local' keyword in the function's definition).
On the other hand there are *identifiers*. They are just strings of
characters that appear in GAP's input (there are certain rules which rule
what is considered an identifier as opposed to an integer or a keyword,
but this does not interest us here). Each variable has a unique
identifier, which is usually called its *name*.
The interesting connection is the one that goes from identifiers to
variables. So for example in the following piece of code there are three
variables whose name is 'a' and we must define to which such variable the
identifier 'a' in the 'Print' statement is referring to.
a := "global a"; f := function () local a; a := "a of f"; h(); end; g := function () local a; a := "a of f"; h := function () Print( a, "\n" ); end; f(); end; g();
There are three variables called 'a', so there are three different
possibilities. The first is to argue that the function 'h' has no local
variable called 'a', so the identifier 'a' must refer to the global
variable (and 'Print' should print "global a").
Another interpretation is that when the 'Print' statement is executed the
last *dynamically* created variable called 'a' is the one of 'f', so the
identifier 'a' in the 'Print' statement should refer to this variable
(and 'Print' should print "a of f"). This point of view is usually
called *dynamical scoping*.
The last interpretation is that when one reads the text the last variable
that was introduced before the 'Print' statement is that of the function
'g', so the identifier 'a' in the 'Print' statement should refer to this
variable (and 'Print' should print "a of g"). This point of view is
usually called *lexical scoping*.
If you try this example in GAP you will see that the last interpretation
is the one adopted in GAP.
Lexical scoping is considered a *good thing* (by the Lisp community,
which initially tried dynamical scoping), because it has the following
property (usually called *alpha-renaming*):
If one replaces the identifier of an argument or a local variable of
a function with another identifier that is not used within the
functions body, the behavior of any program that calls this function
does not change.
For example assume for the moment that GAP uses dynamical scoping. Then
the above example would print "a of f". If we now change the name of the
local variable of 'f' from 'a' to 'aa', calling 'g' would suddenly print
"a of g" (because this becomes the dynamically innermost instance of a
variable called 'a').
Thus lexical scoping confines the scope where the name of a variable
makes any difference to a lexically apparent part of the source text
(hence the name). The philosophy behind this is the idea that a
programming language should be as little surprising as possible. And
having a program's behavior changed simply by renaming a local variable
'i' to 'ThisIsALoopVariable' is surprising.
If we look at Eamonn's code fragment, we see that what he asks for would
violate this property. Suppose for the moment that the definition of the
local variable called 'G' would indeed capture the assignment in
'file.g'. If we now would change the name of this local variable from
'G' to 'H' in the function 'ReadFromFile' but not in 'file.g', then
suddenly the program would (mysteriously) behave different, because now
the assignment in 'file.g' would assign to the global variable with the
name 'G' (or a local variable of the name 'G' that happens to be active
when we execute 'ReadFromFile').
I hope that this rather long explanation impressed you above on the
virtues of lexical scoping that you don't actually want to use the
following hack ;-)
ReadFromFile := function ( name ) local oldG, newG, exists; if IsBound( G ) then oldG := G; fi; exists := READ( name ); newG := G; if IsBound( oldG ) then G := oldG; else Unbind( G ); fi; return newG; end;
Martin Sch"onert, Martin.Schoenert@Math.RWTH-Aachen.DE, +49 241 804551
Lehrstuhl D f"ur Mathematik, Templergraben 64, RWTH, D 51 Aachen, Germany