discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

"interesting" scoping behavior for $ variables

JB
Jordan Brown
Fri, Oct 23, 2020 4:48 PM

When playing with my "tracking" transformation modules, I discovered an
... interesting ... scoping behavior of $ variables.  It seems
undesirable, but I don't immediately see how to fix it.

What "should" the output of this program be?

$x=1;

module foo() {
     $x=2;
     children();
}

foo() {
     x = $x;
     echo(x=x, $x=$x);
}

I expected:

ECHO: x = 2, $x = 2

What it outputs:

ECHO: x = 1, $x = 2

The reason is that the assignments for foo's child block are executed
before any part of foo itself is executed, and then the modules for
foo's children are executed when they are referenced with children(),
after foo's own assignments and during its module execution.

This is a problem for my world-coordinate work, because it means that a
reference to the local matrix inside the top level of a transformation's
child block sees the transformation matrix before that
transformation.  In my implementation:

translate([10,0,0]) {
     p = whereis([0,0,0]);
     echo(p);
}

yields [0,0,0] instead of [10,0,0].  Revar's implementation suffers a
similar fate:

translate([30,40,50]) {
     p = local_translation();
     echo(p);
}

also yields [0,0,0].

There's a workaround:  if the assignment is not in the top level of the
child block then it doesn't get done until the children are referenced:

translate([10,0,0]) union() {
     p = whereis([0,0,0]);
     echo(p);
}

but I don't immediately see a way to work around it transparently,
because the parent module doesn't get any chance at all to execute
before the assignments in the top level of its child block are done.

I suspect that this would be tough to change, because (a) the
child-block's assignments need to be executed before any of the children
are executed and preferably only once, and (b) the parent module may do
$ assignments during its own module-execution phase.  Perhaps the answer
is that the child-block's assignments could be done on each call to
children(), but that seems painful. (But it also seems more correct,
since different invocations of children() could have different values
set into $ variables.)

I haven't a clue whether a "native" implementation of the
world-coordinate-transformation-tracking mechanism could avoid this by
doing its work before the child-block's assignments.

When playing with my "tracking" transformation modules, I discovered an ... interesting ... scoping behavior of $ variables.  It seems undesirable, but I don't immediately see how to fix it. What "should" the output of this program be? $x=1; module foo() { $x=2; children(); } foo() { x = $x; echo(x=x, $x=$x); } I expected: ECHO: x = 2, $x = 2 What it outputs: ECHO: x = 1, $x = 2 The reason is that the assignments for foo's child block are executed before any part of foo itself is executed, and then the modules for foo's children are executed when they are referenced with children(), after foo's own assignments and during its module execution. This is a problem for my world-coordinate work, because it means that a reference to the local matrix inside the top level of a transformation's child block sees the transformation matrix *before* that transformation.  In my implementation: translate([10,0,0]) { p = whereis([0,0,0]); echo(p); } yields [0,0,0] instead of [10,0,0].  Revar's implementation suffers a similar fate: translate([30,40,50]) {     p = local_translation();     echo(p); } also yields [0,0,0]. There's a workaround:  if the assignment is not in the top level of the child block then it doesn't get done until the children are referenced: translate([10,0,0]) union() { p = whereis([0,0,0]); echo(p); } but I don't immediately see a way to work around it transparently, because the parent module doesn't get any chance at all to execute before the assignments in the top level of its child block are done. I suspect that this would be tough to change, because (a) the child-block's assignments need to be executed before any of the children are executed and preferably only once, and (b) the parent module may do $ assignments during its own module-execution phase.  Perhaps the answer is that the child-block's assignments could be done on each call to children(), but that seems painful. (But it also seems more correct, since different invocations of children() could have different values set into $ variables.) I haven't a clue whether a "native" implementation of the world-coordinate-transformation-tracking mechanism could avoid this by doing its work before the child-block's assignments.
RP
Ronaldo Persiano
Fri, Oct 23, 2020 5:10 PM

You will get what you want with:

foo() {
let( x = $x) {
echo(x=x, $x=$x);
}
}

Em sex., 23 de out. de 2020 às 17:49, Jordan Brown <
openscad@jordan.maileater.net> escreveu:

When playing with my "tracking" transformation modules, I discovered an
... interesting ... scoping behavior of $ variables.  It seems undesirable,
but I don't immediately see how to fix it.

What "should" the output of this program be?

$x=1;

module foo() {
$x=2;
children();
}

foo() {
x = $x;
echo(x=x, $x=$x);
}

I expected:

ECHO: x = 2, $x = 2

What it outputs:

ECHO: x = 1, $x = 2

The reason is that the assignments for foo's child block are executed
before any part of foo itself is executed, and then the modules for foo's
children are executed when they are referenced with children(), after foo's
own assignments and during its module execution.

This is a problem for my world-coordinate work, because it means that a
reference to the local matrix inside the top level of a transformation's
child block sees the transformation matrix before that transformation.
In my implementation:

translate([10,0,0]) {
p = whereis([0,0,0]);
echo(p);
}

yields [0,0,0] instead of [10,0,0].  Revar's implementation suffers a
similar fate:

translate([30,40,50]) {
p = local_translation();
echo(p);
}

also yields [0,0,0].

There's a workaround:  if the assignment is not in the top level of the
child block then it doesn't get done until the children are referenced:

translate([10,0,0]) union() {
p = whereis([0,0,0]);
echo(p);
}

but I don't immediately see a way to work around it transparently, because
the parent module doesn't get any chance at all to execute before the
assignments in the top level of its child block are done.

I suspect that this would be tough to change, because (a) the
child-block's assignments need to be executed before any of the children
are executed and preferably only once, and (b) the parent module may do $
assignments during its own module-execution phase.  Perhaps the answer is
that the child-block's assignments could be done on each call to
children(), but that seems painful.  (But it also seems more correct, since
different invocations of children() could have different values set into $
variables.)

I haven't a clue whether a "native" implementation of the
world-coordinate-transformation-tracking mechanism could avoid this by
doing its work before the child-block's assignments.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

You will get what you want with: foo() { let( x = $x) { echo(x=x, $x=$x); } } Em sex., 23 de out. de 2020 às 17:49, Jordan Brown < openscad@jordan.maileater.net> escreveu: > When playing with my "tracking" transformation modules, I discovered an > ... interesting ... scoping behavior of $ variables. It seems undesirable, > but I don't immediately see how to fix it. > > What "should" the output of this program be? > > $x=1; > > module foo() { > $x=2; > children(); > } > > foo() { > x = $x; > echo(x=x, $x=$x); > } > > I expected: > > ECHO: x = 2, $x = 2 > > What it outputs: > > ECHO: x = 1, $x = 2 > > The reason is that the assignments for foo's child block are executed > before any part of foo itself is executed, and then the modules for foo's > children are executed when they are referenced with children(), after foo's > own assignments and during its module execution. > > This is a problem for my world-coordinate work, because it means that a > reference to the local matrix inside the top level of a transformation's > child block sees the transformation matrix *before* that transformation. > In my implementation: > > translate([10,0,0]) { > p = whereis([0,0,0]); > echo(p); > } > > yields [0,0,0] instead of [10,0,0]. Revar's implementation suffers a > similar fate: > > translate([30,40,50]) { > p = local_translation(); > echo(p); > } > > also yields [0,0,0]. > > There's a workaround: if the assignment is not in the top level of the > child block then it doesn't get done until the children are referenced: > > translate([10,0,0]) union() { > p = whereis([0,0,0]); > echo(p); > } > > but I don't immediately see a way to work around it transparently, because > the parent module doesn't get any chance at all to execute before the > assignments in the top level of its child block are done. > > I suspect that this would be tough to change, because (a) the > child-block's assignments need to be executed before any of the children > are executed and preferably only once, and (b) the parent module may do $ > assignments during its own module-execution phase. Perhaps the answer is > that the child-block's assignments could be done on each call to > children(), but that seems painful. (But it also seems more correct, since > different invocations of children() could have different values set into $ > variables.) > > I haven't a clue whether a "native" implementation of the > world-coordinate-transformation-tracking mechanism could avoid this by > doing its work before the child-block's assignments. > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
JB
Jordan Brown
Fri, Oct 23, 2020 5:29 PM

On 10/23/2020 10:10 AM, Ronaldo Persiano wrote:

You will get what you want with:

foo() {
    let( x = $x) {
    echo(x=x, $x=$x);
    }
}

Yes, and also with:

foo() {
    let() {
        x = $x;
        echo(x=x, $x=$x);
    }
}

Both of those work because the assignment of x is inside a child of
foo() rather than in foo()'s child-block top-level assignments.

The goal is to have this be transparent to the caller, and in particular
to allow the caller to use a function that depends on the parent
module's $ variable settings.

On 10/23/2020 10:10 AM, Ronaldo Persiano wrote: > You will get what you want with: > > foo() { >     let( x = $x) { >     echo(x=x, $x=$x); >     } > } Yes, and also with: foo() { let() { x = $x; echo(x=x, $x=$x); } } Both of those work because the assignment of x is inside a child of foo() rather than in foo()'s child-block top-level assignments. The goal is to have this be transparent to the caller, and in particular to allow the caller to use a function that depends on the parent module's $ variable settings.
JB
Jordan Brown
Thu, Sep 21, 2023 5:46 AM

[ Following up on this old thread with new information, primarily for
the message archive. ]

On 10/23/2020 9:48 AM, Jordan Brown wrote:

What "should" the output of this program be?

 $x=1;

 module foo() {
     $x=2;
     children();
 }

 foo() {
     x = $x;
     echo(x=x, $x=$x);
 }

I expected:

 ECHO: x = 2, $x = 2

What it outputs:

 ECHO: x = 1, $x = 2

The reason is that the assignments for foo's child block are executed
before any part of foo itself is executed, and then the modules for
foo's children are executed when they are referenced with children(),
after foo's own assignments and during its module execution.

This behavior seems to have changed since the 2021.01 release; in
2023.06.06 it yields the expected result.

[ Following up on this old thread with new information, primarily for the message archive. ] On 10/23/2020 9:48 AM, Jordan Brown wrote: > > What "should" the output of this program be? > > $x=1; > > module foo() { > $x=2; > children(); > } > > foo() { > x = $x; > echo(x=x, $x=$x); > } > > I expected: > > ECHO: x = 2, $x = 2 > > What it outputs: > > ECHO: x = 1, $x = 2 > > The reason is that the assignments for foo's child block are executed > before any part of foo itself is executed, and then the modules for > foo's children are executed when they are referenced with children(), > after foo's own assignments and during its module execution. > This behavior seems to have changed since the 2021.01 release; in 2023.06.06 it yields the expected result.