[PATCH] More correctly describe the scope of variables

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] More correctly describe the scope of variables

Jouke Witteveen
* NEWS: Use "local" instead of the incorrect "lexically-scoped".
* doc/make.texi: Refer to let/foreach variables as local variables.
---
This is an erratum on the addition of $(let ...). During an early review
of $(let ...), thutt cautioned that it did not implement "full semantic
scoping" [sic]. While I did not understand fully what they meant by
that, I countered that it was not intended to determine a scope based on
for example which file a definition of a rule occurred in, but simply by
the parentheses delimiting the let expression. Only in that sense was it
lexical scoping. Technically, make variables are dynamically scoped.

This patch replaces "lexically scoped" not by "dynamically scoped", but
by "local", since that is the whole point after all. It also includes
variables with local scope (from let and foreach) in several other
places where variables are discussed, and makes explicit that variables
in make are dynamically scoped.

 NEWS          |  4 ++--
 doc/make.texi | 21 +++++++++++++++------
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/NEWS b/NEWS
index 5d71488..5356260 100644
--- a/NEWS
+++ b/NEWS
@@ -35,8 +35,8 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=109&se
   The configure script should verify the compiler has these features.
 
 * New feature: The $(let ...) function
-  This function allows user-defined functions to provide a lexically-scoped
-  set of variables: values can be assigned to these variables from within the
+  This function allows user-defined functions to define a set of local
+  variables: values can be assigned to these variables from within the
   user-defined function and they will not impact global variable assignments.
   Implementation provided by Jouke Witteveen <[hidden email]>
 
diff --git a/doc/make.texi b/doc/make.texi
index fe64ec2..d7891c9 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -276,7 +276,7 @@ Functions for Transforming Text
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
 * Conditional Functions::       Functions that implement conditions.
-* Let Function::                Lexically scoped variables.
+* Let Function::                Local variables.
 * Foreach Function::            Repeat some text with controlled variation.
 * File Function::               Write text to a file.
 * Call Function::               Expand a user-defined function.
@@ -5204,7 +5204,9 @@ variables are called @dfn{macros}.)
 Variables and functions in all parts of a makefile are expanded when
 read, except for in recipes, the right-hand sides of variable
 definitions using @samp{=}, and the bodies of variable definitions
-using the @code{define} directive.@refill
+using the @code{define} directive.  The value a variable expands to is
+that of its most recent definition at the time of expansion.  In other
+words, variables are dynamically scoped.
 
 Variables can represent lists of file names, options to pass to compilers,
 programs to run, directories to look in for source files, directories to
@@ -5792,6 +5794,11 @@ You can specify a value in the makefile, either
 with an assignment (@pxref{Setting, ,Setting Variables}) or with a
 verbatim definition (@pxref{Multi-Line, ,Defining Multi-Line Variables}).@refill
 
+@item
+You can specify a short-lived value with the @code{let} function
+(@pxref{Let Function}) or with the @code{foreach} function
+(@pxref{Foreach Function}).
+
 @item
 Variables in the environment become @code{make} variables.
 @xref{Environment, ,Variables from the Environment}.
@@ -6274,10 +6281,12 @@ the Shell}.@refill
 
 Variable values in @code{make} are usually global; that is, they are the
 same regardless of where they are evaluated (unless they're reset, of
-course).  One exception to that is automatic variables
+course).  Exceptions to that are variables defined with the @code{let}
+function (@pxref{Let Function}) or the @code{foreach} function
+(@pxref{Foreach Function}, and automatic variables
 (@pxref{Automatic Variables}).
 
-The other exception is @dfn{target-specific variable values}.  This
+Another exception are @dfn{target-specific variable values}.  This
 feature allows you to define different values for the same variable,
 based on the target that @code{make} is currently building.  As with
 automatic variables, these values are only available within the context
@@ -7039,7 +7048,7 @@ be substituted.
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
 * Conditional Functions::       Functions that implement conditions.
-* Let Function::                Lexically scoped variables.
+* Let Function::                Local variables.
 * Foreach Function::            Repeat some text with controlled variation.
 * File Function::               Write text to a file.
 * Call Function::               Expand a user-defined function.
@@ -7696,7 +7705,7 @@ the result of the expansion is the expansion of the last argument.
 @node Let Function, Foreach Function, Conditional Functions, Functions
 @section The @code{let} Function
 @findex let
-@cindex variables, lexically scoped
+@cindex variables, local
 
 The @code{let} function provides a means to limit the scope of a
 variable.  The assignment of the named variables in a @code{let}
--
2.29.2


Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] More correctly describe the scope of variables

Pete Dietl
So if you imagine each function call as creating a new environment, an environment being a dictionary of key value pairs that stores variables we define locally AND a “parent pointer” to another environment, then we can define lexical vs dynamic scoping as follows:

Lexical: the parent pointer in a new environment points not one up the function call stack, but to the environment in which the function was defined.

Dynamic: the parent pointer in a new environment points to the previous function call in the stack.

Then we have local vs global scope. Local means the variable is stored in the current environment. Global means that we go all the way up the environments until we get to the top and place the variable there.

Some languages let you explicitly choose to put a variable in the global scope or the local scope.
Take Bash for instance, it is dynamically scoped, but has global and local variables. For example:

bar() {
    echo "a is '$a'"
}

foo() {
    local a='aaaa'
    bar
}

foo

echo "a is '$a'"

This will output:

a is 'aaaa'
a is ''

This is because Bash is dynamically scoped, but explicitly not globally in this case.
Compare to:

bar() {
    echo "a is '$a'"
}

foo() {
    a='aaaa'
    bar
}

foo

echo "a is '$a'"

This will output:

a is 'aaaa'
a is 'aaaa'

This is because by default Bash puts variables into the global scope. When one does this, one cannot tell whether or not the language is dynamically or lexically scoped. But Bash allows us to tell since it has a notion of local vs global variables.

Now for Make, there are no semantics for telling whether or not it is lexically or dynamically scoped. This is because previously the only local variables were the function arguments. Since these arguments are always named the same thing, even if the language were dynamically scoped, successive function calls would create new function argument variables which shadow the previous ones. If you try to use eval to create functions or variables on the fly, it works but both are added to the global scope. 

So, perhaps you could say that make had globally scoped variables only. But now it also has local variables. The question is, with the introduction of the let function, is it using dynamic or lexical scoping? I think it's clear that it is creating local variables (or bindings), but the important thing to know is if it is dynamic or lexical.

On Fri, Dec 25, 2020 at 10:00 AM Jouke Witteveen <[hidden email]> wrote:
* NEWS: Use "local" instead of the incorrect "lexically-scoped".
* doc/make.texi: Refer to let/foreach variables as local variables.
---
This is an erratum on the addition of $(let ...). During an early review
of $(let ...), thutt cautioned that it did not implement "full semantic
scoping" [sic]. While I did not understand fully what they meant by
that, I countered that it was not intended to determine a scope based on
for example which file a definition of a rule occurred in, but simply by
the parentheses delimiting the let expression. Only in that sense was it
lexical scoping. Technically, make variables are dynamically scoped.

This patch replaces "lexically scoped" not by "dynamically scoped", but
by "local", since that is the whole point after all. It also includes
variables with local scope (from let and foreach) in several other
places where variables are discussed, and makes explicit that variables
in make are dynamically scoped.

 NEWS          |  4 ++--
 doc/make.texi | 21 +++++++++++++++------
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/NEWS b/NEWS
index 5d71488..5356260 100644
--- a/NEWS
+++ b/NEWS
@@ -35,8 +35,8 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=109&se
   The configure script should verify the compiler has these features.

 * New feature: The $(let ...) function
-  This function allows user-defined functions to provide a lexically-scoped
-  set of variables: values can be assigned to these variables from within the
+  This function allows user-defined functions to define a set of local
+  variables: values can be assigned to these variables from within the
   user-defined function and they will not impact global variable assignments.
   Implementation provided by Jouke Witteveen <[hidden email]>

diff --git a/doc/make.texi b/doc/make.texi
index fe64ec2..d7891c9 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -276,7 +276,7 @@ Functions for Transforming Text
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
 * Conditional Functions::       Functions that implement conditions.
-* Let Function::                Lexically scoped variables.
+* Let Function::                Local variables.
 * Foreach Function::            Repeat some text with controlled variation.
 * File Function::               Write text to a file.
 * Call Function::               Expand a user-defined function.
@@ -5204,7 +5204,9 @@ variables are called @dfn{macros}.)
 Variables and functions in all parts of a makefile are expanded when
 read, except for in recipes, the right-hand sides of variable
 definitions using @samp{=}, and the bodies of variable definitions
-using the @code{define} directive.@refill
+using the @code{define} directive.  The value a variable expands to is
+that of its most recent definition at the time of expansion.  In other
+words, variables are dynamically scoped.

 Variables can represent lists of file names, options to pass to compilers,
 programs to run, directories to look in for source files, directories to
@@ -5792,6 +5794,11 @@ You can specify a value in the makefile, either
 with an assignment (@pxref{Setting, ,Setting Variables}) or with a
 verbatim definition (@pxref{Multi-Line, ,Defining Multi-Line Variables}).@refill

+@item
+You can specify a short-lived value with the @code{let} function
+(@pxref{Let Function}) or with the @code{foreach} function
+(@pxref{Foreach Function}).
+
 @item
 Variables in the environment become @code{make} variables.
 @xref{Environment, ,Variables from the Environment}.
@@ -6274,10 +6281,12 @@ the Shell}.@refill

 Variable values in @code{make} are usually global; that is, they are the
 same regardless of where they are evaluated (unless they're reset, of
-course).  One exception to that is automatic variables
+course).  Exceptions to that are variables defined with the @code{let}
+function (@pxref{Let Function}) or the @code{foreach} function
+(@pxref{Foreach Function}, and automatic variables
 (@pxref{Automatic Variables}).

-The other exception is @dfn{target-specific variable values}.  This
+Another exception are @dfn{target-specific variable values}.  This
 feature allows you to define different values for the same variable,
 based on the target that @code{make} is currently building.  As with
 automatic variables, these values are only available within the context
@@ -7039,7 +7048,7 @@ be substituted.
 * Text Functions::              General-purpose text manipulation functions.
 * File Name Functions::         Functions for manipulating file names.
 * Conditional Functions::       Functions that implement conditions.
-* Let Function::                Lexically scoped variables.
+* Let Function::                Local variables.
 * Foreach Function::            Repeat some text with controlled variation.
 * File Function::               Write text to a file.
 * Call Function::               Expand a user-defined function.
@@ -7696,7 +7705,7 @@ the result of the expansion is the expansion of the last argument.
 @node Let Function, Foreach Function, Conditional Functions, Functions
 @section The @code{let} Function
 @findex let
-@cindex variables, lexically scoped
+@cindex variables, local

 The @code{let} function provides a means to limit the scope of a
 variable.  The assignment of the named variables in a @code{let}
--
2.29.2


Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] More correctly describe the scope of variables

Jouke Witteveen
On Fri, Dec 25, 2020 at 7:58 PM Pete Dietl <[hidden email]> wrote:
> Now for Make, there are no semantics for telling whether or not it is lexically or dynamically scoped. This is because previously the only local variables were the function arguments. Since these arguments are always named the same thing, even if the language were dynamically scoped, successive function calls would create new function argument variables which shadow the previous ones. If you try to use eval to create functions or variables on the fly, it works but both are added to the global scope.

There is an easy demonstration (adapted from
https://en.wikipedia.org/wiki/Scope_(computer_science) ):
----
x=1
g=$(info $x)
f=$(foreach x,3,$g)
$f
$(info $x)
----
Here, `foreach` can be replaced by `let`. The first output is "3",
demonstrating dynamic scoping.
Behavior aside, the source code of GNU make is structured so that make
has dynamic scoping.

Apart from these remarks, I agree with your analysis and have also
observed that assignments in $(eval) will assign at the global scope
(and not the scope at the top of the stack). I am not sure whether
that is a feature or a bug.

Regards,
- Jouke

>
> On Fri, Dec 25, 2020 at 10:00 AM Jouke Witteveen <[hidden email]> wrote:
>>
>> * NEWS: Use "local" instead of the incorrect "lexically-scoped".
>> * doc/make.texi: Refer to let/foreach variables as local variables.
>> ---
>> This is an erratum on the addition of $(let ...). During an early review
>> of $(let ...), thutt cautioned that it did not implement "full semantic
>> scoping" [sic]. While I did not understand fully what they meant by
>> that, I countered that it was not intended to determine a scope based on
>> for example which file a definition of a rule occurred in, but simply by
>> the parentheses delimiting the let expression. Only in that sense was it
>> lexical scoping. Technically, make variables are dynamically scoped.
>>
>> This patch replaces "lexically scoped" not by "dynamically scoped", but
>> by "local", since that is the whole point after all. It also includes
>> variables with local scope (from let and foreach) in several other
>> places where variables are discussed, and makes explicit that variables
>> in make are dynamically scoped.
>>
>>  NEWS          |  4 ++--
>>  doc/make.texi | 21 +++++++++++++++------
>>  2 files changed, 17 insertions(+), 8 deletions(-)
>>

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] More correctly describe the scope of variables

Pete Dietl
A very good point!

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] More correctly describe the scope of variables

Jouke Witteveen
In reply to this post by Jouke Witteveen
On Fri, Dec 25, 2020 at 7:00 PM Jouke Witteveen <[hidden email]> wrote:

>
> * NEWS: Use "local" instead of the incorrect "lexically-scoped".
> * doc/make.texi: Refer to let/foreach variables as local variables.
> ---
> This is an erratum on the addition of $(let ...). During an early review
> of $(let ...), thutt cautioned that it did not implement "full semantic
> scoping" [sic]. While I did not understand fully what they meant by
> that, I countered that it was not intended to determine a scope based on
> for example which file a definition of a rule occurred in, but simply by
> the parentheses delimiting the let expression. Only in that sense was it
> lexical scoping. Technically, make variables are dynamically scoped.
>
> This patch replaces "lexically scoped" not by "dynamically scoped", but
> by "local", since that is the whole point after all. It also includes
> variables with local scope (from let and foreach) in several other
> places where variables are discussed, and makes explicit that variables
> in make are dynamically scoped.

I think this is ready to go in and that it would be wise to not
release with the unchanged NEWS.
Was this patch simply missed due to end-of-year activities?

Regards,
- Jouke

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] More correctly describe the scope of variables

Jouke Witteveen
On Fri, Jan 29, 2021 at 8:37 AM Jouke Witteveen <[hidden email]> wrote:

> On Fri, Dec 25, 2020 at 7:00 PM Jouke Witteveen <[hidden email]> wrote:
> >
> > * NEWS: Use "local" instead of the incorrect "lexically-scoped".
> > * doc/make.texi: Refer to let/foreach variables as local variables.
> > ---
> > This is an erratum on the addition of $(let ...). During an early review
> > of $(let ...), thutt cautioned that it did not implement "full semantic
> > scoping" [sic]. While I did not understand fully what they meant by
> > that, I countered that it was not intended to determine a scope based on
> > for example which file a definition of a rule occurred in, but simply by
> > the parentheses delimiting the let expression. Only in that sense was it
> > lexical scoping. Technically, make variables are dynamically scoped.
> >
> > This patch replaces "lexically scoped" not by "dynamically scoped", but
> > by "local", since that is the whole point after all. It also includes
> > variables with local scope (from let and foreach) in several other
> > places where variables are discussed, and makes explicit that variables
> > in make are dynamically scoped.
>
> I think this is ready to go in and that it would be wise to not
> release with the unchanged NEWS.
> Was this patch simply missed due to end-of-year activities?

Another month, another reminder.

Cheers,
- Jouke

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] More correctly describe the scope of variables

Paul Smith-20
On Wed, 2021-03-03 at 20:47 +0100, Jouke Witteveen wrote:
> Another month, another reminder.

I pushed these changes yesterday; thanks for the reminders! :)