A
adrianv
Sat, Dec 26, 2020 3:02 PM
I think it's a strong argument that you can always insert empty geometry if
you need it, but there's no way to remove empty geometry once it's been
created, so I agree with nophead that false if and empty for should behave
like echo and assert and not create any geometry. Even though Jordan
pointed out how either choice can break backwards compatibility, my feeling
is that the current version (where for and if generate empty geometry)
breaks more things than the alternative, and the fixes for those broken
things are more painful, since you have to repeat blocks of code instead of
just adding an "else union();"
nophead wrote
I think a false if or an empty for() should not create empty geometry,
just
as echo() doesn't. It is more backwards compatible and you can always
force
empty geometry with else union();
On Sat, 26 Dec 2020 at 14:40, Hans L <
On Fri, Dec 25, 2020 at 10:14 AM adrianv <
I recall this being discussed a while back and I'm not sure about what
the
ultimate conclusion was, and I can't remember what the issue was that
gave
rise to the change.
On 12/25/2020 12:12 PM, Jordan Brown wrote:
I don't think it returns an empty object in 2019.05. (Check the CSG
tree.)
I mis-wrote. I don't think it returns nothing in 2019.05. In both
2019.05 and the current build, if(false) yields an empty group() in the
CSG
tree. As do echo(), for()-that-never-runs, and empty modules.
The difference is in what intersection() and difference() do with
children that don't contain any solid-generators.
2019.05's difference() and intersection() ignore children that don't
contain anything that generates a solid.
Thus echo, if(false), "translate([0,0,0]);", empty modules, et cetera
are
all ignored.
An intersection or difference of a cube or other solid, that yields
nothing, is not ignored.
Jordan is correct here, the old version basically had no concept of
empty
geometry and treated it the same as "no geometry" (eg an echo or assert
node without children).
So I guess the issue is now whether or not an untaken conditional should
be counted as empty geometry or not geometry at all.
Without enabling the experimental feature of "lazy-union", a group
essentially implies a union. So an empty group is treated much like
"union() {}" which means "the empty set" of geometry.
Hans
OpenSCAD mailing list
I think it's a strong argument that you can always insert empty geometry if
you need it, but there's no way to remove empty geometry once it's been
created, so I agree with nophead that false if and empty for should behave
like echo and assert and not create any geometry. Even though Jordan
pointed out how either choice can break backwards compatibility, my feeling
is that the current version (where for and if generate empty geometry)
breaks more things than the alternative, and the fixes for those broken
things are more painful, since you have to repeat blocks of code instead of
just adding an "else union();"
nophead wrote
> I think a false if or an empty for() should not create empty geometry,
> just
> as echo() doesn't. It is more backwards compatible and you can always
> force
> empty geometry with else union();
>
> On Sat, 26 Dec 2020 at 14:40, Hans L <
> thehans@
> > wrote:
>
>>
>> On Fri, Dec 25, 2020 at 10:14 AM adrianv <
> avm4@
> > wrote:
>>
>>> I recall this being discussed a while back and I'm not sure about what
>>> the
>>> ultimate conclusion was, and I can't remember what the issue was that
>>> gave
>>> rise to the change.
>>>
>>
>> The change was introduced by
>> https://github.com/openscad/openscad/pull/3342
>> which was a fix for at least 4 existing issues:
>> https://github.com/openscad/openscad/issues/666
>> https://github.com/openscad/openscad/issues/3311
>> https://github.com/openscad/openscad/issues/3312
>> https://github.com/openscad/openscad/issues/3416
>>
>> On Fri, Dec 25, 2020 at 2:38 PM Jordan Brown <
>>
> openscad@.maileater
>> wrote:
>>
>>> On 12/25/2020 12:12 PM, Jordan Brown wrote:
>>>
>>> I don't think it returns an empty object in 2019.05. (Check the CSG
>>> tree.)
>>>
>>>
>>> I mis-wrote. I don't think it returns *nothing* in 2019.05. In both
>>> 2019.05 and the current build, if(false) yields an empty group() in the
>>> CSG
>>> tree. As do echo(), for()-that-never-runs, and empty modules.
>>>
>>> The difference is in what intersection() and difference() do with
>>> children that don't contain any solid-generators.
>>>
>>> 2019.05's difference() and intersection() ignore children that don't
>>> contain anything that generates a solid.
>>> Thus echo, if(false), "translate([0,0,0]);", empty modules, et cetera
>>> are
>>> all ignored.
>>> An intersection or difference of a cube or other solid, that yields
>>> nothing, is *not* ignored.
>>>
>>
>> Jordan is correct here, the old version basically had no concept of
>> empty
>> geometry and treated it the same as "no geometry" (eg an echo or assert
>> node without children).
>> So I guess the issue is now whether or not an untaken conditional should
>> be counted as empty geometry or not geometry at all.
>>
>> Without enabling the experimental feature of "lazy-union", a group
>> essentially implies a union. So an empty group is treated much like
>> "union() {}" which means "the empty set" of geometry.
>>
>> Hans
>>
>> _______________________________________________
>> OpenSCAD mailing list
>>
> Discuss@.openscad
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@.openscad
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
Sent from: http://forum.openscad.org/
JB
Jordan Brown
Sat, Dec 26, 2020 10:28 PM
[ I'm not sure whether my commentary below really adds value. However,
it helped me to work through some of the issues and think about the
issues in a somewhat structured way, so I'll send it out. The TL;DR
version is that there probably needs to be a kind of nothingness that is
between "absolutely nothing" and "an empty set". ]
On 12/26/2020 7:02 AM, adrianv wrote:
I think it's a strong argument that you can always insert empty geometry if
you need it, but there's no way to remove empty geometry once it's been
created, so I agree with nophead that false if and empty for should behave
like echo and assert and not create any geometry. Even though Jordan
pointed out how either choice can break backwards compatibility, my feeling
is that the current version (where for and if generate empty geometry)
breaks more things than the alternative, and the fixes for those broken
things are more painful, since you have to repeat blocks of code instead of
just adding an "else union();"
It's hard to talk about different kinds of "nothing", and terms like
"object" are overloaded, so I'll define a couple of terms for what I'm
about to say:
When I say "nothing", it means that there is absolutely nothing
generated. Comments generate nothing. Assignments generate nothing.
The program "", the program consisting of no text at all, generates nothing.
When I say "set", I mean a (possibly empty) collection of points.
When I say "empty set", it means that there are operations, but that the
result contains no points. Examples include union() {} and
intersection() { cube(); translate([10,0,0]) cube() }.
When I say "module-invocation-like construct", I mean anything that
looks like "xxx(...);" or "xxx(...) { ... }". As a special case, I
include "if(...) { ... } else { ... }".
--- I think the next section describes fact, not opinion. ---
In 2019.05:
- Every module-invocation-like construct generates exactly one set.
There is no module-invocation-like construct that will generate
nothing. In particular, echo(), assert(), and failing if() yield
empty sets.
- intersection, difference, and intersection_for ignore children that
are empty sets. Thus, unlike in mathematics, the intersection of X
and an empty set is X. If E is an empty set, difference() { E; Y;
Z; } is the same as difference() { Y; Z; }.
In RC3:
- echo() and assert() yield nothing. Except: they count as children
for purposes of $children and children().
- failing if() yields an empty set.
- Successful if() yields at least an empty set, even if its contents
yield nothing.
- A module yields at least an empty set, even if its contents yield
nothing.
- intersection and difference respect children that are empty sets.
As in mathematics, the intersection of an empty set and anything is
an empty set. The difference of an empty set and anything is an
empty set.
Note that RC3 has made two changes:
- intersection and difference respect empty sets.
- There are now module-invocation-like constructs that yield nothing -
echo() and assert().
--- Now for opinion ---
I think that the change to intersection and difference is correct. It
matches mathematics, and it avoids perverse results in (admittedly
contrived) cases. For instance, in 2019.05:
module foo(d) {
intersection() {
cube();
intersection() {
cube();
translate([d,0,0]) cube();
}
}
}
will yield part of a cube if d is -1 through 1, but will yield a full
cube if d is outside that range. In particular, foo(0.999) will yield a
very thin slice of a cube, but foo(1.001) will yield a full cube. (The
behavior of foo(1) is left as a question for philosophers.)
I have mixed feelings about the change to echo() and assert(). On the
one hand, it's intuitively sensible. These are clearly not attempts to
describe geometry. On the other hand, they introduce perverse cases.
echo() produces nothing, but if(true)echo() produces an empty set. A
module consisting only of an invocation of echo() produces an empty
set. children(i) where the specified child is an echo yields nothing.
Yeah, I'll have to come down on the side of "no" on that one. I can't
even find a clear way to describe the behavior of echo(). It's not
nothing, but it's not an empty set. (But see below for an idea.)
As for the "failing if yields nothing" proposal, I have to come down
solidly against. (But again, see below.) Although again it's
intuitively sensible, it makes a hash of what $children means and the
indexing of children. What does this print? (Note that rands(0,1,1)[0]
yields a random number between 0 and 1.)
module count() {
echo($children);
echo($children);
}
count() {
if (rands(0,1,1)[0] > 0.5) {
cube();
}
}
Before "failing if yields nothing", you don't need to evaluate the
children to determine the value of $children. With it, you have to
evaluate them to determine whether or not they generate nothing. And if
you recursively apply nothingness (so that an if whose body yields
nothing, yields nothing, and so a module whose body yields nothing,
yields nothing) then you have to evaluate arbitrarily deeply. What if
some of that evaluation is erroneous when done in the wrong context?
value = -1;
module foo() {
echo($children);
if (value >= 0) {
children(0);
}
}
function safe_sqrt(v) = assert(v >= 0) sqrt(v);
foo() {
if (safe_sqrt(value) > 1) {
cube();
}
}
When do you calculate the value of $children? Before doing anything
with the module? What if the children's behavior depends on $ variables
set by the module? Do you re-evaluate $children each time it's used?
Do you re-evaluate the child list each time children() is used?
module perverse() {
$mychildren = $children;
echo($children);
}
perverse() {
if ($mychildren == 0) {
cube();
}
}
That's purposely perverse, but I've had programs that had children that
conditionally supplied geometry depending on $ variables set by the parent.
module foo() {
for ($i=[0:3]) {
children();
}
}
foo() {
if ($i != 0) {
cube();
}
}
The best idea I come up with to comply with some of the intuitively
desirable behavior of echo() and failing if() is to define a new kind of
nothingness. Call it NULL.
- echo() and failing if(), among others, would yield NULL.
- NULL is not nothing. It counts as a child. (So all module-like
invocations yield children, avoiding the $children problem described
above.)
- Intersection and difference would ignore NULL children.
- Ideally, many operations on NULL would yield NULL.
o A module whose contents all yield NULL (or with no contents at
all) would yield NULL.
o An "if" whose children all yield NULL (or with no children)
would yield NULL.
o children(i), where the corresponding child is NULL, would yield
NULL.
- However, boolean operations on NULL should probably yield empty
sets. In particular, union() {} should yield an empty set.
intersection() { NULL } would ignore the NULL child, and would then
yield an empty set.
That seems to address most of the concerns. The fact that echo() counts
as a child might cause confusion for some.
(Note: I suspect that PR#3555 may effectively implement some of this,
though defined in terms of counting nodes in the AST tree for $children
and children() purposes.)
HOWEVER, I'll point out that "null" and similar concepts have been very
difficult subjects in other languages. C programmers often mess up the
difference between NULL and empty strings. JavaScript programmers are
treated to something like five different kinds of nothingness -
undefined, null, {}, [], and "" - and confusion involving them is
common. Programmers in many languages often have trouble with the idea
of arrays with no entries, without even bringing in the concept of NULL.
[ I'm not sure whether my commentary below really adds value. However,
it helped me to work through some of the issues and think about the
issues in a somewhat structured way, so I'll send it out. The TL;DR
version is that there probably needs to be a kind of nothingness that is
between "absolutely nothing" and "an empty set". ]
On 12/26/2020 7:02 AM, adrianv wrote:
> I think it's a strong argument that you can always insert empty geometry if
> you need it, but there's no way to remove empty geometry once it's been
> created, so I agree with nophead that false if and empty for should behave
> like echo and assert and not create any geometry. Even though Jordan
> pointed out how either choice can break backwards compatibility, my feeling
> is that the current version (where for and if generate empty geometry)
> breaks more things than the alternative, and the fixes for those broken
> things are more painful, since you have to repeat blocks of code instead of
> just adding an "else union();"
It's hard to talk about different kinds of "nothing", and terms like
"object" are overloaded, so I'll define a couple of terms for what I'm
about to say:
When I say "nothing", it means that there is absolutely nothing
generated. Comments generate nothing. Assignments generate nothing.
The program "", the program consisting of no text at all, generates nothing.
When I say "set", I mean a (possibly empty) collection of points.
When I say "empty set", it means that there are operations, but that the
result contains no points. Examples include union() {} and
intersection() { cube(); translate([10,0,0]) cube() }.
When I say "module-invocation-like construct", I mean anything that
looks like "xxx(...);" or "xxx(...) { ... }". As a special case, I
include "if(...) { ... } else { ... }".
--- I think the next section describes fact, not opinion. ---
In 2019.05:
* Every module-invocation-like construct generates exactly one set.
There is no module-invocation-like construct that will generate
nothing. In particular, echo(), assert(), and failing if() yield
empty sets.
* intersection, difference, and intersection_for ignore children that
are empty sets. Thus, unlike in mathematics, the intersection of X
and an empty set is X. If E is an empty set, difference() { E; Y;
Z; } is the same as difference() { Y; Z; }.
In RC3:
* echo() and assert() yield nothing. Except: they count as children
for purposes of $children and children().
* failing if() yields an empty set.
* Successful if() yields at least an empty set, even if its contents
yield nothing.
* A module yields at least an empty set, even if its contents yield
nothing.
* intersection and difference respect children that are empty sets.
As in mathematics, the intersection of an empty set and anything is
an empty set. The difference of an empty set and anything is an
empty set.
Note that RC3 has made two changes:
* intersection and difference respect empty sets.
* There are now module-invocation-like constructs that yield nothing -
echo() and assert().
--- Now for opinion ---
I think that the change to intersection and difference is correct. It
matches mathematics, and it avoids perverse results in (admittedly
contrived) cases. For instance, in 2019.05:
module foo(d) {
intersection() {
cube();
intersection() {
cube();
translate([d,0,0]) cube();
}
}
}
will yield part of a cube if d is -1 through 1, but will yield a full
cube if d is outside that range. In particular, foo(0.999) will yield a
very thin slice of a cube, but foo(1.001) will yield a full cube. (The
behavior of foo(1) is left as a question for philosophers.)
I have mixed feelings about the change to echo() and assert(). On the
one hand, it's intuitively sensible. These are clearly not attempts to
describe geometry. On the other hand, they introduce perverse cases.
echo() produces nothing, but if(true)echo() produces an empty set. A
module consisting only of an invocation of echo() produces an empty
set. children(i) where the specified child is an echo yields nothing.
Yeah, I'll have to come down on the side of "no" on that one. I can't
even find a clear way to describe the behavior of echo(). It's not
nothing, but it's not an empty set. (But see below for an idea.)
As for the "failing if yields nothing" proposal, I have to come down
solidly against. (But again, see below.) Although again it's
intuitively sensible, it makes a hash of what $children means and the
indexing of children. What does this print? (Note that rands(0,1,1)[0]
yields a random number between 0 and 1.)
module count() {
echo($children);
echo($children);
}
count() {
if (rands(0,1,1)[0] > 0.5) {
cube();
}
}
Before "failing if yields nothing", you don't need to evaluate the
children to determine the value of $children. With it, you have to
evaluate them to determine whether or not they generate nothing. And if
you recursively apply nothingness (so that an if whose body yields
nothing, yields nothing, and so a module whose body yields nothing,
yields nothing) then you have to evaluate arbitrarily deeply. What if
some of that evaluation is erroneous when done in the wrong context?
value = -1;
module foo() {
echo($children);
if (value >= 0) {
children(0);
}
}
function safe_sqrt(v) = assert(v >= 0) sqrt(v);
foo() {
if (safe_sqrt(value) > 1) {
cube();
}
}
When do you calculate the value of $children? Before doing anything
with the module? What if the children's behavior depends on $ variables
set by the module? Do you re-evaluate $children each time it's used?
Do you re-evaluate the child list each time children() is used?
module perverse() {
$mychildren = $children;
echo($children);
}
perverse() {
if ($mychildren == 0) {
cube();
}
}
That's purposely perverse, but I've had programs that had children that
conditionally supplied geometry depending on $ variables set by the parent.
module foo() {
for ($i=[0:3]) {
children();
}
}
foo() {
if ($i != 0) {
cube();
}
}
---
The best idea I come up with to comply with some of the intuitively
desirable behavior of echo() and failing if() is to define a new kind of
nothingness. Call it NULL.
* echo() and failing if(), among others, would yield NULL.
* NULL is not nothing. It counts as a child. (So all module-like
invocations yield children, avoiding the $children problem described
above.)
* Intersection and difference would ignore NULL children.
* Ideally, many operations on NULL would yield NULL.
o A module whose contents all yield NULL (or with no contents at
all) would yield NULL.
o An "if" whose children all yield NULL (or with no children)
would yield NULL.
o children(i), where the corresponding child is NULL, would yield
NULL.
* However, boolean operations on NULL should probably yield empty
sets. In particular, union() {} should yield an empty set.
intersection() { NULL } would ignore the NULL child, and would then
yield an empty set.
That seems to address most of the concerns. The fact that echo() counts
as a child might cause confusion for some.
(Note: I suspect that PR#3555 may effectively implement some of this,
though defined in terms of counting nodes in the AST tree for $children
and children() purposes.)
HOWEVER, I'll point out that "null" and similar concepts have been very
difficult subjects in other languages. C programmers often mess up the
difference between NULL and empty strings. JavaScript programmers are
treated to something like five different kinds of nothingness -
undefined, null, {}, [], and "" - and confusion involving them is
common. Programmers in many languages often have trouble with the idea
of arrays with no entries, without even bringing in the concept of NULL.
A
adrianv
Sun, Dec 27, 2020 12:52 AM
I agree that operations should interact correctly with empty sets.
Current behavior of RC3 is that echo() and assert() return your NULL. They
behave like children but generate no geometry. Note that echo is only
actually run if the child is actually instantiated.
module foo() {
intersection(){
children(0);
children(1);
}
}
foo() {
cube();
echo("child 1");
cylinder();
}
Above code produces a cube and displays "child 1". If you swap the echo and
the cylinder then you get the intersection of cube with cylinder and nothing
is printed. This is arguably somewhat confusing. Consider this:
foo() {
cube();
cylinder();
assert(false, "fail!");
}
The assert never runs. Also arguably confusing. But this behavior is
consistent between versions. It seems like the truth is that passing
assert or echo as children is just weird and confusing. Is there any use in
doing this?
My proposal, which is consistent with your suggestion, is that false if and
empty for should behave the same as echo, namely that they behave as your
NULL, which already exists, like it or not. And I think the behavior of
echo (and assert) is far more confusing than my proposed behavior for if()
and for() would be.
However, in thinking carefully about this I wonder if any of the options
really avoids confusion. It seems like the main case where this matters
with the standard language and with empty if or for is for intersection(),
because an actual empty object eliminates your model, and there's no clean
way to fix it. With union or difference it doesn't matter. (Is there any
way to add an "everything" object to openscad?)
But for user written modules, a variety of things may happen. Consider a
module spread() that places its child i at position [10*i,0,0]. If you
include echo() as a child you get a blank space. And the same thing happens
(in both language versions) if you pass "if (false) cube();" This seems
like unexpected behavior to me. But the only way around it is to give the
module the ability to identify modules as NULL. And this starts to get
messy. Most modules will want to count the non-null children and skip all
the NULL ones...which amounts to treating NULL children as nonexistent.
Does this end up any different than actually not passing the children at
all?
JordanBrown wrote
The best idea I come up with to comply with some of the intuitively
desirable behavior of echo() and failing if() is to define a new kind of
nothingness. Call it NULL.
- echo() and failing if(), among others, would yield NULL.
- NULL is not nothing. It counts as a child. (So all module-like
invocations yield children, avoiding the $children problem described
above.)
- Intersection and difference would ignore NULL children.
- Ideally, many operations on NULL would yield NULL.
o A module whose contents all yield NULL (or with no contents at
all) would yield NULL.
o An "if" whose children all yield NULL (or with no children)
would yield NULL.
o children(i), where the corresponding child is NULL, would yield
NULL.
- However, boolean operations on NULL should probably yield empty
sets. In particular, union() {} should yield an empty set.
intersection() { NULL } would ignore the NULL child, and would then
yield an empty set.
That seems to address most of the concerns. The fact that echo() counts
as a child might cause confusion for some.
(Note: I suspect that PR#3555 may effectively implement some of this,
though defined in terms of counting nodes in the AST tree for $children
and children() purposes.)
I agree that operations should interact correctly with empty sets.
Current behavior of RC3 is that echo() and assert() return your NULL. They
behave like children but generate no geometry. Note that echo is only
actually run if the child is actually instantiated.
module foo() {
intersection(){
children(0);
children(1);
}
}
foo() {
cube();
echo("child 1");
cylinder();
}
Above code produces a cube and displays "child 1". If you swap the echo and
the cylinder then you get the intersection of cube with cylinder and nothing
is printed. This is arguably somewhat confusing. Consider this:
foo() {
cube();
cylinder();
assert(false, "fail!");
}
The assert never runs. Also arguably confusing. But this behavior is
consistent between versions. It seems like the truth is that passing
assert or echo as children is just weird and confusing. Is there any use in
doing this?
My proposal, which is consistent with your suggestion, is that false if and
empty for should behave the same as echo, namely that they behave as your
NULL, which already exists, like it or not. And I think the behavior of
echo (and assert) is far more confusing than my proposed behavior for if()
and for() would be.
However, in thinking carefully about this I wonder if any of the options
really avoids confusion. It seems like the main case where this matters
with the standard language and with empty if or for is for intersection(),
because an actual empty object eliminates your model, and there's no clean
way to fix it. With union or difference it doesn't matter. (Is there any
way to add an "everything" object to openscad?)
But for user written modules, a variety of things may happen. Consider a
module spread() that places its child i at position [10*i,0,0]. If you
include echo() as a child you get a blank space. And the same thing happens
(in both language versions) if you pass "if (false) cube();" This seems
like unexpected behavior to me. But the only way around it is to give the
module the ability to identify modules as NULL. And this starts to get
messy. Most modules will want to count the non-null children and skip all
the NULL ones...which amounts to treating NULL children as nonexistent.
Does this end up any different than actually not passing the children at
all?
JordanBrown wrote
> The best idea I come up with to comply with some of the intuitively
> desirable behavior of echo() and failing if() is to define a new kind of
> nothingness. Call it NULL.
>
> * echo() and failing if(), among others, would yield NULL.
> * NULL is not nothing. It counts as a child. (So all module-like
> invocations yield children, avoiding the $children problem described
> above.)
> * Intersection and difference would ignore NULL children.
> * Ideally, many operations on NULL would yield NULL.
> o A module whose contents all yield NULL (or with no contents at
> all) would yield NULL.
> o An "if" whose children all yield NULL (or with no children)
> would yield NULL.
> o children(i), where the corresponding child is NULL, would yield
> NULL.
> * However, boolean operations on NULL should probably yield empty
> sets. In particular, union() {} should yield an empty set.
> intersection() { NULL } would ignore the NULL child, and would then
> yield an empty set.
>
> That seems to address most of the concerns. The fact that echo() counts
> as a child might cause confusion for some.
>
> (Note: I suspect that PR#3555 may effectively implement some of this,
> though defined in terms of counting nodes in the AST tree for $children
> and children() purposes.)
--
Sent from: http://forum.openscad.org/
HL
Hans L
Sun, Dec 27, 2020 2:12 AM
Keep in mind that echo and assert can both have children. They only yield
"NULL" if no geometry child nodes are given. This was partly addressed for
another issue: https://github.com/openscad/openscad/issues/3131 which is
different since the 2019 release.
In the 2019 release, assert would render children if given, but echo would
not. Now they both do.
That's why I mentioned in my first post in this thread "(eg an echo or
assert node without children).", [emphasis now added] but may have
been too subtle on that point.
So if you want to use echo or assert in a way that doesn't affect the child
count then you can nest your actual geometry as children of them (probably
preferably on the first element[s])
foo() {
assert(false, "fail!") cube();
cylinder();
}
On Sat, Dec 26, 2020 at 6:53 PM adrianv avm4@cornell.edu wrote:
I agree that operations should interact correctly with empty sets.
Current behavior of RC3 is that echo() and assert() return your NULL. They
behave like children but generate no geometry. Note that echo is only
actually run if the child is actually instantiated.
module foo() {
intersection(){
children(0);
children(1);
}
}
foo() {
cube();
echo("child 1");
cylinder();
}
Above code produces a cube and displays "child 1". If you swap the echo
and
the cylinder then you get the intersection of cube with cylinder and
nothing
is printed. This is arguably somewhat confusing. Consider this:
foo() {
cube();
cylinder();
assert(false, "fail!");
}
The assert never runs. Also arguably confusing. But this behavior is
consistent between versions. It seems like the truth is that passing
assert or echo as children is just weird and confusing. Is there any use
in
doing this?
My proposal, which is consistent with your suggestion, is that false if and
empty for should behave the same as echo, namely that they behave as your
NULL, which already exists, like it or not. And I think the behavior of
echo (and assert) is far more confusing than my proposed behavior for if()
and for() would be.
However, in thinking carefully about this I wonder if any of the options
really avoids confusion. It seems like the main case where this matters
with the standard language and with empty if or for is for intersection(),
because an actual empty object eliminates your model, and there's no clean
way to fix it. With union or difference it doesn't matter. (Is there any
way to add an "everything" object to openscad?)
But for user written modules, a variety of things may happen. Consider a
module spread() that places its child i at position [10*i,0,0]. If you
include echo() as a child you get a blank space. And the same thing
happens
(in both language versions) if you pass "if (false) cube();" This seems
like unexpected behavior to me. But the only way around it is to give the
module the ability to identify modules as NULL. And this starts to get
messy. Most modules will want to count the non-null children and skip all
the NULL ones...which amounts to treating NULL children as nonexistent.
Does this end up any different than actually not passing the children at
all?
JordanBrown wrote
The best idea I come up with to comply with some of the intuitively
desirable behavior of echo() and failing if() is to define a new kind of
nothingness. Call it NULL.
- echo() and failing if(), among others, would yield NULL.
- NULL is not nothing. It counts as a child. (So all module-like
invocations yield children, avoiding the $children problem described
above.)
- Intersection and difference would ignore NULL children.
- Ideally, many operations on NULL would yield NULL.
o A module whose contents all yield NULL (or with no contents at
all) would yield NULL.
o An "if" whose children all yield NULL (or with no children)
would yield NULL.
o children(i), where the corresponding child is NULL, would yield
NULL.
- However, boolean operations on NULL should probably yield empty
sets. In particular, union() {} should yield an empty set.
intersection() { NULL } would ignore the NULL child, and would then
yield an empty set.
That seems to address most of the concerns. The fact that echo() counts
as a child might cause confusion for some.
(Note: I suspect that PR#3555 may effectively implement some of this,
though defined in terms of counting nodes in the AST tree for $children
and children() purposes.)
Keep in mind that echo and assert can both have children. They only yield
"NULL" if no geometry child nodes are given. This was partly addressed for
another issue: https://github.com/openscad/openscad/issues/3131 which is
different since the 2019 release.
In the 2019 release, assert would render children if given, but echo would
not. Now they both do.
That's why I mentioned in my first post in this thread "(eg an echo or
assert node **without children**).", [emphasis now added] but may have
been too subtle on that point.
So if you want to use echo or assert in a way that doesn't affect the child
count then you can nest your actual geometry as children of them (probably
preferably on the first element[s])
foo() {
assert(false, "fail!") cube();
cylinder();
}
On Sat, Dec 26, 2020 at 6:53 PM adrianv <avm4@cornell.edu> wrote:
> I agree that operations should interact correctly with empty sets.
>
> Current behavior of RC3 is that echo() and assert() return your NULL. They
> behave like children but generate no geometry. Note that echo is only
> actually run if the child is actually instantiated.
>
> module foo() {
> intersection(){
> children(0);
> children(1);
> }
> }
>
> foo() {
> cube();
> echo("child 1");
> cylinder();
>
> }
>
> Above code produces a cube and displays "child 1". If you swap the echo
> and
> the cylinder then you get the intersection of cube with cylinder and
> nothing
> is printed. This is arguably somewhat confusing. Consider this:
>
> foo() {
> cube();
> cylinder();
> assert(false, "fail!");
> }
>
> The assert never runs. Also arguably confusing. But this behavior is
> consistent between versions. It seems like the truth is that passing
> assert or echo as children is just weird and confusing. Is there any use
> in
> doing this?
>
> My proposal, which is consistent with your suggestion, is that false if and
> empty for should behave the same as echo, namely that they behave as your
> NULL, which already exists, like it or not. And I think the behavior of
> echo (and assert) is far more confusing than my proposed behavior for if()
> and for() would be.
>
> However, in thinking carefully about this I wonder if any of the options
> really avoids confusion. It seems like the main case where this matters
> with the standard language and with empty if or for is for intersection(),
> because an actual empty object eliminates your model, and there's no clean
> way to fix it. With union or difference it doesn't matter. (Is there any
> way to add an "everything" object to openscad?)
>
> But for user written modules, a variety of things may happen. Consider a
> module spread() that places its child i at position [10*i,0,0]. If you
> include echo() as a child you get a blank space. And the same thing
> happens
> (in both language versions) if you pass "if (false) cube();" This seems
> like unexpected behavior to me. But the only way around it is to give the
> module the ability to identify modules as NULL. And this starts to get
> messy. Most modules will want to count the non-null children and skip all
> the NULL ones...which amounts to treating NULL children as nonexistent.
> Does this end up any different than actually not passing the children at
> all?
>
>
> JordanBrown wrote
> > The best idea I come up with to comply with some of the intuitively
> > desirable behavior of echo() and failing if() is to define a new kind of
> > nothingness. Call it NULL.
> >
> > * echo() and failing if(), among others, would yield NULL.
> > * NULL is not nothing. It counts as a child. (So all module-like
> > invocations yield children, avoiding the $children problem described
> > above.)
> > * Intersection and difference would ignore NULL children.
> > * Ideally, many operations on NULL would yield NULL.
> > o A module whose contents all yield NULL (or with no contents at
> > all) would yield NULL.
> > o An "if" whose children all yield NULL (or with no children)
> > would yield NULL.
> > o children(i), where the corresponding child is NULL, would yield
> > NULL.
> > * However, boolean operations on NULL should probably yield empty
> > sets. In particular, union() {} should yield an empty set.
> > intersection() { NULL } would ignore the NULL child, and would then
> > yield an empty set.
> >
> > That seems to address most of the concerns. The fact that echo() counts
> > as a child might cause confusion for some.
> >
> > (Note: I suspect that PR#3555 may effectively implement some of this,
> > though defined in terms of counting nodes in the AST tree for $children
> > and children() purposes.)
>
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
JB
Jordan Brown
Sun, Dec 27, 2020 2:26 AM
[ Resend from correct address. ]
On 12/26/2020 4:52 PM, adrianv wrote:
Current behavior of RC3 is that echo() and assert() return your NULL. They
behave like children but generate no geometry.
And intersection() in particular ignores children with no geometry.
Note that echo is only actually run if the child is actually instantiated.
Yes. None of the children are run unless instantiated. Violating that
risks running them in cases where their parents know they won't work.
It seems like the truth is that passing assert or echo as children is just weird and confusing.
Somewhat. But they obey the general rule: they run when you say
children(i) and refer to them.
Is there any use in doing this?
You might leave an echo as a placeholder for future work.
union() {
part1();
echo("need to write part 2");
}
Even for a selective parent, it might make sense. Consider for instance
a decorated_cube() module that builds a cube and and translates and
rotates each of its six children to be on its faces.
decorated_cube() {
face1();
face2();
echo("need to design face 3");
...
}
My proposal, which is consistent with your suggestion, is that false if and
empty for should behave the same as echo, namely that they behave as your
NULL,
Yes, that's what I was thinking.
which already exists, like it or not.
Well, in the RC, which is 95% existing but not 100%. (I think my
primary contribution to this discussion might be in giving it a name.)
And I think the behavior of echo (and assert) is far more confusing than my proposed behavior for if() and for() would be.
Perhaps. One of the things that was making my head hurt was that
echo("foo");
and
if (true) echo("foo");
currently behave differently in the RC, as children of intersection().
Similarly that a module that exists only to wrap around echo() behaves
differently than a plain echo().
Even stranger is that plain echo generates NULL but
if (false) echo("foo");
generates an empty set.
However, in thinking carefully about this I wonder if any of the options
really avoids confusion.
It seems like the main case where this matters with the standard language and with empty if or for is for intersection(), because an actual empty object eliminates your model, and there's no clean way to fix it.
Intersecting with an actual empty-set object (say, the intersection of
two disjoint objects) absolutely should eliminate your model.
YAFIYGI. The question is what constructs should yield empty sets.
With union or difference it doesn't matter.
With difference it matters a little, because the first child is special
(it's positive).
difference() {
if (false);
cube();
}
yields a cube in 2019.05 and an empty set in RC3.
But for user written modules, a variety of things may happen. Consider a
module spread() that places its child i at position [10*i,0,0].
Yep. Exactly the confusing case I was thinking of.
But the only way around it is to give the module the ability to identify modules as NULL.
But you can't know that it's NULL until you instantiate it, and then
it's too late.
I think Very Bad Things(tm) will happen if you try to have any of these
constructs not yield children. The rule needs to be simple: they get
evaluated when and if their parents say to evaluate them. You have to
just understand that they do generate children.
[ Resend from correct address. ]
On 12/26/2020 4:52 PM, adrianv wrote:
> Current behavior of RC3 is that echo() and assert() return your NULL. They
> behave like children but generate no geometry.
And intersection() in particular ignores children with no geometry.
> Note that echo is only actually run if the child is actually instantiated.
Yes. None of the children are run unless instantiated. Violating that
risks running them in cases where their parents know they won't work.
> It seems like the truth is that passing assert or echo as children is just weird and confusing.
Somewhat. But they obey the general rule: they run when you say
children(i) and refer to them.
> Is there any use in doing this?
You might leave an echo as a placeholder for future work.
union() {
part1();
echo("need to write part 2");
}
Even for a selective parent, it might make sense. Consider for instance
a decorated_cube() module that builds a cube and and translates and
rotates each of its six children to be on its faces.
decorated_cube() {
face1();
face2();
echo("need to design face 3");
...
}
> My proposal, which is consistent with your suggestion, is that false if and
> empty for should behave the same as echo, namely that they behave as your
> NULL,
Yes, that's what I was thinking.
> which already exists, like it or not.
Well, in the RC, which is 95% existing but not 100%. (I think my
primary contribution to this discussion might be in giving it a name.)
> And I think the behavior of echo (and assert) is far more confusing than my proposed behavior for if() and for() would be.
Perhaps. One of the things that was making my head hurt was that
echo("foo");
and
if (true) echo("foo");
currently behave differently in the RC, as children of intersection().
Similarly that a module that exists only to wrap around echo() behaves
differently than a plain echo().
Even stranger is that plain echo generates NULL but
if (false) echo("foo");
generates an empty set.
> However, in thinking carefully about this I wonder if any of the options
> really avoids confusion.
Nope :-(.
> It seems like the main case where this matters with the standard language and with empty if or for is for intersection(), because an actual empty object eliminates your model, and there's no clean way to fix it.
Intersecting with an *actual* empty-set object (say, the intersection of
two disjoint objects) absolutely *should* eliminate your model.
YAFIYGI. The question is what constructs should yield empty sets.
> With union or difference it doesn't matter.
With difference it matters a little, because the first child is special
(it's positive).
difference() {
if (false);
cube();
}
yields a cube in 2019.05 and an empty set in RC3.
> But for user written modules, a variety of things may happen. Consider a
> module spread() that places its child i at position [10*i,0,0].
Yep. Exactly the confusing case I was thinking of.
> But the only way around it is to give the module the ability to identify modules as NULL.
But you can't know that it's NULL until you instantiate it, and then
it's too late.
I think Very Bad Things(tm) will happen if you try to have any of these
constructs not yield children. The rule needs to be simple: they get
evaluated when and if their parents say to evaluate them. You have to
just understand that they *do* generate children.
JB
Jordan Brown
Sun, Dec 27, 2020 2:41 AM
On 12/26/2020 6:12 PM, Hans L wrote:
Keep in mind that echo and assert can both have children.
You just made my head explode. Now there are brains and blood all over
my keyboard.
Concur that echo() and assert() should behave the same with respect to
their children. (Slight inclination towards "it's an error", but not
enough to fight.)
On 12/26/2020 6:12 PM, Hans L wrote:
> Keep in mind that echo and assert can both have children.
You just made my head explode. Now there are brains and blood all over
my keyboard.
Concur that echo() and assert() should behave the same with respect to
their children. (Slight inclination towards "it's an error", but not
enough to fight.)
A
adrianv
Sun, Dec 27, 2020 2:58 AM
It seems like the truth is that passing assert or echo as children is
just weird and confusing.
Somewhat. But they obey the general rule: they run when you say
children(i) and refer to them.
Yes, everything makes sense if you understand the technical details. But
for a casual user it's going to be surprising and puzzling, I think.
The note that assert and echo take children is interesting. It makes
sense...but I don't think I realized it. (Even though that's how assert and
echo act in let() statements. In fact, you have to use assert with children
in functions if you want to avoid dummy variables. The same is true of
echo...but I usually only insert it for debugging so I'm not bothered by the
dummy vars.)
Is there any use in doing this?
You might leave an echo as a placeholder for future work.
union() {
part1();
echo("need to write part 2");
}
I think in my own work I'd be more inclined to stick a cube() or something
in as a placeholder than text. Or just a comment.
My proposal, which is consistent with your suggestion, is that false if
and
empty for should behave the same as echo, namely that they behave as your
NULL,
Yes, that's what I was thinking.
which already exists, like it or not.
Well, in the RC, which is 95% existing but not 100%. (I think my
primary contribution to this discussion might be in giving it a name.)
And I think the behavior of echo (and assert) is far more confusing than
my proposed behavior for if() and for() would be.
Perhaps. One of the things that was making my head hurt was that
echo("foo");
and
if (true) echo("foo");
currently behave differently in the RC, as children of intersection().
Similarly that a module that exists only to wrap around echo() behaves
differently than a plain echo().
Even stranger is that plain echo generates NULL but
if (false) echo("foo");
generates an empty set.
Wouldn't these things be "fixed" by having if statements not create
geometry?
However, in thinking carefully about this I wonder if any of the options
really avoids confusion.
It seems like the main case where this matters with the standard language
and with empty if or for is for intersection(), because an actual empty
object eliminates your model, and there's no clean way to fix it.
Intersecting with an actual empty-set object (say, the intersection of
two disjoint objects) absolutely should eliminate your model.
YAFIYGI. The question is what constructs should yield empty sets.
With union or difference it doesn't matter.
With difference it matters a little, because the first child is special
(it's positive).
difference() {
if (false);
cube();
}
yields a cube in 2019.05 and an empty set in RC3.
I'm in agreement that intersection with empty set should give the empty set.
The problem is that there's no way to specify the complement of the empty
set. So I can't do
intersection(){
if (condition) thing(); else everything();
otherthing();
}
Being able to do that would solve the problem with intersections in a simple
manner. I assume that it's impossible to implement the everything() object.
For difference, yes, it matters what the first object is, and your example
does give a case where things change. But it's not an example that seems
like it has a use case. I can't think of a situation where you want A-B in
one case and B in another case. It's just not likely. It's also simple
because you've got only one possible meaningful condition, whether A is
enabled or not. Consider an intersection like:
intersection(){
mainthing();
if (parm1) thing1();
if (parm2) thing2();
if (parm3) thing3();
if (parm4) thing4();
}
Now if I need to handle this it explodes into a real mess because I have to
consider every combination of the 4 variables separately, so I need 2^4=16
cases instead of 4 cases. Presumably the only realistic way to solve this
is to write an "everything" module that is a superset of mainthing(), or do
something like
intersection(){
mainthing();
if (parm1) thing1(); else mainthing();
if (parm2) thing2(); else mainthing();
if (parm3) thing3(); else mainthing();
if (parm4) thing4(); else mainthing();
}
Of course, the other solution is to avoid intersection() for cases like this
and use difference() instead. Then there's no problem.
But for user written modules, a variety of things may happen. Consider a
module spread() that places its child i at position [10*i,0,0].
Yep. Exactly the confusing case I was thinking of.
But the only way around it is to give the module the ability to identify
modules as NULL.
But you can't know that it's NULL until you instantiate it, and then
it's too late.
Yeah. At least this is not a change from the old behavior. That is, the
spread module behaves the same in the RC3 as it does in the 2019 version.
In either case, the empty geometry or the NULL object get treated as a child
that renders as nothing, and you get a gap. I'm left wondering if it
really matters whether if(false) and empty for return an empty set or a
NULL, since most of the time it will give the same result anyway.
--
Sent from: http://forum.openscad.org/
JordanBrown wrote
>> It seems like the truth is that passing assert or echo as children is
>> just weird and confusing.
>
> Somewhat. But they obey the general rule: they run when you say
> children(i) and refer to them.
Yes, everything makes sense if you understand the technical details. But
for a casual user it's going to be surprising and puzzling, I think.
The note that assert and echo take children is interesting. It makes
sense...but I don't think I realized it. (Even though that's how assert and
echo act in let() statements. In fact, you have to use assert with children
in functions if you want to avoid dummy variables. The same is true of
echo...but I usually only insert it for debugging so I'm not bothered by the
dummy vars.)
>> Is there any use in doing this?
>
> You might leave an echo as a placeholder for future work.
>
> union() {
> part1();
> echo("need to write part 2");
> }
I think in my own work I'd be more inclined to stick a cube() or something
in as a placeholder than text. Or just a comment.
>> My proposal, which is consistent with your suggestion, is that false if
>> and
>> empty for should behave the same as echo, namely that they behave as your
>> NULL,
>
> Yes, that's what I was thinking.
>
>> which already exists, like it or not.
>
> Well, in the RC, which is 95% existing but not 100%. (I think my
> primary contribution to this discussion might be in giving it a name.)
What is the missing 5%?
>> And I think the behavior of echo (and assert) is far more confusing than
>> my proposed behavior for if() and for() would be.
>
> Perhaps. One of the things that was making my head hurt was that
>
> echo("foo");
>
> and
>
> if (true) echo("foo");
>
> currently behave differently in the RC, as children of intersection().
> Similarly that a module that exists only to wrap around echo() behaves
> differently than a plain echo().
>
> Even stranger is that plain echo generates NULL but
>
> if (false) echo("foo");
>
> generates an empty set.
Wouldn't these things be "fixed" by having if statements not create
geometry?
>> However, in thinking carefully about this I wonder if any of the options
>> really avoids confusion.
>
> Nope :-(.
>
>> It seems like the main case where this matters with the standard language
>> and with empty if or for is for intersection(), because an actual empty
>> object eliminates your model, and there's no clean way to fix it.
>
> Intersecting with an *actual* empty-set object (say, the intersection of
> two disjoint objects) absolutely *should* eliminate your model.
> YAFIYGI. The question is what constructs should yield empty sets.
>
>> With union or difference it doesn't matter.
>
> With difference it matters a little, because the first child is special
> (it's positive).
>
> difference() {
> if (false);
> cube();
> }
>
> yields a cube in 2019.05 and an empty set in RC3.
I'm in agreement that intersection with empty set should give the empty set.
The problem is that there's no way to specify the complement of the empty
set. So I can't do
intersection(){
if (condition) thing(); else everything();
otherthing();
}
Being able to do that would solve the problem with intersections in a simple
manner. I assume that it's impossible to implement the everything() object.
For difference, yes, it matters what the first object is, and your example
does give a case where things change. But it's not an example that seems
like it has a use case. I can't think of a situation where you want A-B in
one case and B in another case. It's just not likely. It's also simple
because you've got only one possible meaningful condition, whether A is
enabled or not. Consider an intersection like:
intersection(){
mainthing();
if (parm1) thing1();
if (parm2) thing2();
if (parm3) thing3();
if (parm4) thing4();
}
Now if I need to handle this it explodes into a real mess because I have to
consider every combination of the 4 variables separately, so I need 2^4=16
cases instead of 4 cases. Presumably the only realistic way to solve this
is to write an "everything" module that is a superset of mainthing(), or do
something like
intersection(){
mainthing();
if (parm1) thing1(); else mainthing();
if (parm2) thing2(); else mainthing();
if (parm3) thing3(); else mainthing();
if (parm4) thing4(); else mainthing();
}
Of course, the other solution is to avoid intersection() for cases like this
and use difference() instead. Then there's no problem.
>> But for user written modules, a variety of things may happen. Consider a
>> module spread() that places its child i at position [10*i,0,0].
>
> Yep. Exactly the confusing case I was thinking of.
>
>> But the only way around it is to give the module the ability to identify
>> modules as NULL.
>
> But you can't know that it's NULL until you instantiate it, and then
> it's too late.
Yeah. At least this is not a change from the old behavior. That is, the
spread module behaves the same in the RC3 as it does in the 2019 version.
In either case, the empty geometry or the NULL object get treated as a child
that renders as nothing, and you get a gap. I'm left wondering if it
really matters whether if(false) and empty for return an empty set or a
NULL, since most of the time it will give the same result anyway.
--
Sent from: http://forum.openscad.org/
JB
Jordan Brown
Mon, Dec 28, 2020 2:59 AM
[ Sigh. Not my day. First I sent this from the wrong address, then
when I went to resend it from the right address I resent the one of
Adrian's messages instead of this one. ]
On 12/26/2020 6:58 PM, adrianv wrote:
It seems like the truth is that passing assert or echo as children is
just weird and confusing.
Somewhat. But they obey the general rule: they run when you say
children(i) and refer to them.
Yes, everything makes sense if you understand the technical details. But
for a casual user it's going to be surprising and puzzling, I think.
Yes.
What I'm saying is that if it's going to be surprising and puzzling, at
least there should be a simple rule.
(And I think that all variations that have gotten past the first level
of discussion have that simple rule, that all module-like invocations
yield children.)
I think in my own work I'd be more inclined to stick a cube() or
something in as a placeholder than text. Or just a comment.
A comment won't be enough if you're leaving a placeholder for child #3 of 6.
which already exists, like it or not.
Well, in the RC, which is 95% existing but not 100%. (I think my
primary contribution to this discussion might be in giving it a name.)
I wasn't saying that the functionality wasn't in the RC... but rather
that the RC is an RC, not a release, so doesn't 100% exist. Behavior in
a non-release can change; behavior in a release should normally remain
constant.
(But: the RC has NULL functionality for echo, assert, intersection, and
difference, but not for if, for, and modules. Hans has a PR for if.)
[ if(true)echo() and if(false)echo() are different from echo() ]
Wouldn't these things be "fixed" by having if statements not create
geometry?
Well, by having failing if not generate geometry - having it generate
NULL - and by having successful if (or else) yield whatever its contents
yield (which might or might not be NULL).
I'm in agreement that intersection with empty set should give the
empty set. The problem is that there's no way to specify the
complement of the empty set. So I can't do
intersection(){
if (condition) thing(); else everything();
otherthing();
}
NULL would address this particular need, because a failing if would
yield NULL, and intersection would be defined to ignore NULL. So for
intersection purposes, NULL would be sort of like everything().
For difference, yes, it matters what the first object is, and your example
does give a case where things change. But it's not an example that seems
like it has a use case.
Agreed. It only has a confusion case, where somebody has put an echo()
as the first child to a difference.
I'm left wondering if it really matters whether if(false) and empty
for return an empty set or a NULL, since most of the time it will give
the same result anyway.
Intersection is the big one.
[ Sigh. Not my day. First I sent this from the wrong address, then
when I went to resend it from the right address I resent the one of
Adrian's messages instead of this one. ]
On 12/26/2020 6:58 PM, adrianv wrote:
> JordanBrown wrote
>>> It seems like the truth is that passing assert or echo as children is
>>> just weird and confusing.
>> Somewhat. But they obey the general rule: they run when you say
>> children(i) and refer to them.
> Yes, everything makes sense if you understand the technical details. But
> for a casual user it's going to be surprising and puzzling, I think.
Yes.
What I'm saying is that if it's going to be surprising and puzzling, at
least there should be a simple rule.
(And I think that all variations that have gotten past the first level
of discussion have that simple rule, that all module-like invocations
yield children.)
> I think in my own work I'd be more inclined to stick a cube() or
> something in as a placeholder than text. Or just a comment.
A comment won't be enough if you're leaving a placeholder for child #3 of 6.
>>> which already exists, like it or not.
>> Well, in the RC, which is 95% existing but not 100%. (I think my
>> primary contribution to this discussion might be in giving it a name.)
> What is the missing 5%?
I wasn't saying that the functionality wasn't in the RC... but rather
that the RC is an RC, not a release, so doesn't 100% exist. Behavior in
a non-release can change; behavior in a release should normally remain
constant.
(But: the RC has NULL functionality for echo, assert, intersection, and
difference, but not for if, for, and modules. Hans has a PR for if.)
> [ if(true)echo() and if(false)echo() are different from echo() ]
> Wouldn't these things be "fixed" by having if statements not create
> geometry?
Well, by having failing if not generate geometry - having it generate
NULL - and by having successful if (or else) yield whatever its contents
yield (which might or might not be NULL).
> I'm in agreement that intersection with empty set should give the
> empty set. The problem is that there's no way to specify the
> complement of the empty set. So I can't do
> intersection(){
> if (condition) thing(); else everything();
> otherthing();
> }
NULL would address this particular need, because a failing if would
yield NULL, and intersection would be defined to ignore NULL. So for
intersection purposes, NULL would be sort of like everything().
> For difference, yes, it matters what the first object is, and your example
> does give a case where things change. But it's not an example that seems
> like it has a use case.
Agreed. It only has a confusion case, where somebody has put an echo()
as the first child to a difference.
> I'm left wondering if it really matters whether if(false) and empty
> for return an empty set or a NULL, since most of the time it will give
> the same result anyway.
Intersection is the big one.
A
adrianv
Thu, Jan 7, 2021 3:33 AM
In RC4, if (false) doesn't create a group in the tree, so intersection works
the way it did before.
JordanBrown wrote
[ Sigh. Not my day. First I sent this from the wrong address, then
when I went to resend it from the right address I resent the one of
Adrian's messages instead of this one. ]
On 12/26/2020 6:58 PM, adrianv wrote:
It seems like the truth is that passing assert or echo as children is
just weird and confusing.
Somewhat. But they obey the general rule: they run when you say
children(i) and refer to them.
Yes, everything makes sense if you understand the technical details. But
for a casual user it's going to be surprising and puzzling, I think.
Yes.
What I'm saying is that if it's going to be surprising and puzzling, at
least there should be a simple rule.
(And I think that all variations that have gotten past the first level
of discussion have that simple rule, that all module-like invocations
yield children.)
I think in my own work I'd be more inclined to stick a cube() or
something in as a placeholder than text. Or just a comment.
A comment won't be enough if you're leaving a placeholder for child #3 of
6.
which already exists, like it or not.
Well, in the RC, which is 95% existing but not 100%. (I think my
primary contribution to this discussion might be in giving it a name.)
I wasn't saying that the functionality wasn't in the RC... but rather
that the RC is an RC, not a release, so doesn't 100% exist. Behavior in
a non-release can change; behavior in a release should normally remain
constant.
(But: the RC has NULL functionality for echo, assert, intersection, and
difference, but not for if, for, and modules. Hans has a PR for if.)
[ if(true)echo() and if(false)echo() are different from echo() ]
Wouldn't these things be "fixed" by having if statements not create
geometry?
Well, by having failing if not generate geometry - having it generate
NULL - and by having successful if (or else) yield whatever its contents
yield (which might or might not be NULL).
I'm in agreement that intersection with empty set should give the
empty set. The problem is that there's no way to specify the
complement of the empty set. So I can't do
intersection(){
if (condition) thing(); else everything();
otherthing();
}
NULL would address this particular need, because a failing if would
yield NULL, and intersection would be defined to ignore NULL. So for
intersection purposes, NULL would be sort of like everything().
For difference, yes, it matters what the first object is, and your
example
does give a case where things change. But it's not an example that seems
like it has a use case.
Agreed. It only has a confusion case, where somebody has put an echo()
as the first child to a difference.
I'm left wondering if it really matters whether if(false) and empty
for return an empty set or a NULL, since most of the time it will give
the same result anyway.
Intersection is the big one.
OpenSCAD mailing list
In RC4, if (false) doesn't create a group in the tree, so intersection works
the way it did before.
JordanBrown wrote
> [ Sigh. Not my day. First I sent this from the wrong address, then
> when I went to resend it from the right address I resent the one of
> Adrian's messages instead of this one. ]
>
> On 12/26/2020 6:58 PM, adrianv wrote:
>> JordanBrown wrote
>>>> It seems like the truth is that passing assert or echo as children is
>>>> just weird and confusing.
>>> Somewhat. But they obey the general rule: they run when you say
>>> children(i) and refer to them.
>> Yes, everything makes sense if you understand the technical details. But
>> for a casual user it's going to be surprising and puzzling, I think.
>
> Yes.
>
> What I'm saying is that if it's going to be surprising and puzzling, at
> least there should be a simple rule.
>
> (And I think that all variations that have gotten past the first level
> of discussion have that simple rule, that all module-like invocations
> yield children.)
>
>> I think in my own work I'd be more inclined to stick a cube() or
>> something in as a placeholder than text. Or just a comment.
>
> A comment won't be enough if you're leaving a placeholder for child #3 of
> 6.
>
>>>> which already exists, like it or not.
>>> Well, in the RC, which is 95% existing but not 100%. (I think my
>>> primary contribution to this discussion might be in giving it a name.)
>> What is the missing 5%?
>
> I wasn't saying that the functionality wasn't in the RC... but rather
> that the RC is an RC, not a release, so doesn't 100% exist. Behavior in
> a non-release can change; behavior in a release should normally remain
> constant.
>
> (But: the RC has NULL functionality for echo, assert, intersection, and
> difference, but not for if, for, and modules. Hans has a PR for if.)
>
>> [ if(true)echo() and if(false)echo() are different from echo() ]
>> Wouldn't these things be "fixed" by having if statements not create
>> geometry?
>
> Well, by having failing if not generate geometry - having it generate
> NULL - and by having successful if (or else) yield whatever its contents
> yield (which might or might not be NULL).
>
>> I'm in agreement that intersection with empty set should give the
>> empty set. The problem is that there's no way to specify the
>> complement of the empty set. So I can't do
>> intersection(){
>> if (condition) thing(); else everything();
>> otherthing();
>> }
>
> NULL would address this particular need, because a failing if would
> yield NULL, and intersection would be defined to ignore NULL. So for
> intersection purposes, NULL would be sort of like everything().
>
>> For difference, yes, it matters what the first object is, and your
>> example
>> does give a case where things change. But it's not an example that seems
>> like it has a use case.
>
> Agreed. It only has a confusion case, where somebody has put an echo()
> as the first child to a difference.
>
>
>> I'm left wondering if it really matters whether if(false) and empty
>> for return an empty set or a NULL, since most of the time it will give
>> the same result anyway.
>
> Intersection is the big one.
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@.openscad
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
Sent from: http://forum.openscad.org/
NH
nop head
Thu, Jan 7, 2021 9:57 AM
Not quite because if(true) echo() has changed to return the empty set.
On Thu, 7 Jan 2021 at 03:34, adrianv avm4@cornell.edu wrote:
In RC4, if (false) doesn't create a group in the tree, so intersection
works
the way it did before.
JordanBrown wrote
[ Sigh. Not my day. First I sent this from the wrong address, then
when I went to resend it from the right address I resent the one of
Adrian's messages instead of this one. ]
On 12/26/2020 6:58 PM, adrianv wrote:
It seems like the truth is that passing assert or echo as children is
just weird and confusing.
Somewhat. But they obey the general rule: they run when you say
children(i) and refer to them.
Yes, everything makes sense if you understand the technical details.
for a casual user it's going to be surprising and puzzling, I think.
Yes.
What I'm saying is that if it's going to be surprising and puzzling, at
least there should be a simple rule.
(And I think that all variations that have gotten past the first level
of discussion have that simple rule, that all module-like invocations
yield children.)
I think in my own work I'd be more inclined to stick a cube() or
something in as a placeholder than text. Or just a comment.
A comment won't be enough if you're leaving a placeholder for child #3 of
6.
which already exists, like it or not.
Well, in the RC, which is 95% existing but not 100%. (I think my
primary contribution to this discussion might be in giving it a name.)
I wasn't saying that the functionality wasn't in the RC... but rather
that the RC is an RC, not a release, so doesn't 100% exist. Behavior in
a non-release can change; behavior in a release should normally remain
constant.
(But: the RC has NULL functionality for echo, assert, intersection, and
difference, but not for if, for, and modules. Hans has a PR for if.)
[ if(true)echo() and if(false)echo() are different from echo() ]
Wouldn't these things be "fixed" by having if statements not create
geometry?
Well, by having failing if not generate geometry - having it generate
NULL - and by having successful if (or else) yield whatever its contents
yield (which might or might not be NULL).
I'm in agreement that intersection with empty set should give the
empty set. The problem is that there's no way to specify the
complement of the empty set. So I can't do
intersection(){
if (condition) thing(); else everything();
otherthing();
}
NULL would address this particular need, because a failing if would
yield NULL, and intersection would be defined to ignore NULL. So for
intersection purposes, NULL would be sort of like everything().
For difference, yes, it matters what the first object is, and your
example
does give a case where things change. But it's not an example that
Agreed. It only has a confusion case, where somebody has put an echo()
as the first child to a difference.
I'm left wondering if it really matters whether if(false) and empty
for return an empty set or a NULL, since most of the time it will give
the same result anyway.
Intersection is the big one.
OpenSCAD mailing list
Not quite because if(true) echo() has changed to return the empty set.
On Thu, 7 Jan 2021 at 03:34, adrianv <avm4@cornell.edu> wrote:
> In RC4, if (false) doesn't create a group in the tree, so intersection
> works
> the way it did before.
>
>
> JordanBrown wrote
> > [ Sigh. Not my day. First I sent this from the wrong address, then
> > when I went to resend it from the right address I resent the one of
> > Adrian's messages instead of this one. ]
> >
> > On 12/26/2020 6:58 PM, adrianv wrote:
> >> JordanBrown wrote
> >>>> It seems like the truth is that passing assert or echo as children is
> >>>> just weird and confusing.
> >>> Somewhat. But they obey the general rule: they run when you say
> >>> children(i) and refer to them.
> >> Yes, everything makes sense if you understand the technical details.
> But
> >> for a casual user it's going to be surprising and puzzling, I think.
> >
> > Yes.
> >
> > What I'm saying is that if it's going to be surprising and puzzling, at
> > least there should be a simple rule.
> >
> > (And I think that all variations that have gotten past the first level
> > of discussion have that simple rule, that all module-like invocations
> > yield children.)
> >
> >> I think in my own work I'd be more inclined to stick a cube() or
> >> something in as a placeholder than text. Or just a comment.
> >
> > A comment won't be enough if you're leaving a placeholder for child #3 of
> > 6.
> >
> >>>> which already exists, like it or not.
> >>> Well, in the RC, which is 95% existing but not 100%. (I think my
> >>> primary contribution to this discussion might be in giving it a name.)
> >> What is the missing 5%?
> >
> > I wasn't saying that the functionality wasn't in the RC... but rather
> > that the RC is an RC, not a release, so doesn't 100% exist. Behavior in
> > a non-release can change; behavior in a release should normally remain
> > constant.
> >
> > (But: the RC has NULL functionality for echo, assert, intersection, and
> > difference, but not for if, for, and modules. Hans has a PR for if.)
> >
> >> [ if(true)echo() and if(false)echo() are different from echo() ]
> >> Wouldn't these things be "fixed" by having if statements not create
> >> geometry?
> >
> > Well, by having failing if not generate geometry - having it generate
> > NULL - and by having successful if (or else) yield whatever its contents
> > yield (which might or might not be NULL).
> >
> >> I'm in agreement that intersection with empty set should give the
> >> empty set. The problem is that there's no way to specify the
> >> complement of the empty set. So I can't do
> >> intersection(){
> >> if (condition) thing(); else everything();
> >> otherthing();
> >> }
> >
> > NULL would address this particular need, because a failing if would
> > yield NULL, and intersection would be defined to ignore NULL. So for
> > intersection purposes, NULL would be sort of like everything().
> >
> >> For difference, yes, it matters what the first object is, and your
> >> example
> >> does give a case where things change. But it's not an example that
> seems
> >> like it has a use case.
> >
> > Agreed. It only has a confusion case, where somebody has put an echo()
> > as the first child to a difference.
> >
> >
> >> I'm left wondering if it really matters whether if(false) and empty
> >> for return an empty set or a NULL, since most of the time it will give
> >> the same result anyway.
> >
> > Intersection is the big one.
> >
> >
> > _______________________________________________
> > OpenSCAD mailing list
>
> > Discuss@.openscad
>
> > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>