RP
Ronaldo Persiano
Sat, Jul 25, 2020 12:32 PM
In the stable version all that echo false:
echo(true==1);
echo((0==0)==1);
echo(false==0);
echo((0==1)==0);
In the version 2020.07.02, all echo true!
It seems that the identities true==1 and false==0 were valid in ancient
versions.
Why the change? Is it intentional?
That breaks some search() of my codes.
In the stable version all that echo false:
echo(true==1);
echo((0==0)==1);
echo(false==0);
echo((0==1)==0);
In the version 2020.07.02, all echo true!
It seems that the identities true==1 and false==0 were valid in ancient
versions.
Why the change? Is it intentional?
That breaks some search() of my codes.
P
Parkinbot
Sat, Jul 25, 2020 2:00 PM
With 2019.12.21 I get:
echo(true==1); // false
echo(true==0); // false
this means not more and not less: a boolean is not a number (apples are no
pears) and you could also write:
echo(true=="1"); // false
echo(true=="0"); // false
Thus any code testing a condition like "true == x" imposes an implicit type
check. Changing this behavior can break such checks as well as reveal buggy
code written under the assumption Booleans are represented as numbers.
In my libs I have tons of code that uses some backward compatible testing
for lists like "a[0]!=undef". Obviously it has the pitfall:
a=[undef, 1,2,3];
But, I can live with it :-), since using is_list() will break even more code
as it hits people using older versions of OpenSCAD who are usally less
experienced programmers.
However, of course you are right: There is no obvious reason to drop the
Boolean data type and introduce old fashioned and error prone semantics in
OpenSCAD.
--
Sent from: http://forum.openscad.org/
With 2019.12.21 I get:
echo(true==1); // false
echo(true==0); // false
this means not more and not less: a boolean is not a number (apples are no
pears) and you could also write:
echo(true=="1"); // false
echo(true=="0"); // false
Thus any code testing a condition like "true == x" imposes an implicit type
check. Changing this behavior can break such checks as well as reveal buggy
code written under the assumption Booleans are represented as numbers.
In my libs I have tons of code that uses some backward compatible testing
for lists like "a[0]!=undef". Obviously it has the pitfall:
a=[undef, 1,2,3];
But, I can live with it :-), since using is_list() will break even more code
as it hits people using older versions of OpenSCAD who are usally less
experienced programmers.
However, of course you are right: There is no obvious reason to drop the
Boolean data type and introduce old fashioned and error prone semantics in
OpenSCAD.
--
Sent from: http://forum.openscad.org/
HL
Hans L
Sat, Jul 25, 2020 4:52 PM
This was part of a change to make comparisons more consistent in general.
See my comment here for more explanation:
https://github.com/openscad/openscad/pull/3164#issuecomment-569158586
(Basically try <= or >= operations between 0 / 1 and false / true on
your older versions, and tell me if that makes sense)
Can you provide a self contained example where the change causes a
problem in actual usage?
Boolean types are not "dropped", but there is type coercion which is
now applied consistently between numbers and bools.
is_bool() and is_num() can also be used to determine exact type if needed.
On Sat, Jul 25, 2020 at 9:01 AM Parkinbot rudolf@digitaldocument.de wrote:
With 2019.12.21 I get:
echo(true==1); // false
echo(true==0); // false
this means not more and not less: a boolean is not a number (apples are no
pears) and you could also write:
echo(true=="1"); // false
echo(true=="0"); // false
Thus any code testing a condition like "true == x" imposes an implicit type
check. Changing this behavior can break such checks as well as reveal buggy
code written under the assumption Booleans are represented as numbers.
In my libs I have tons of code that uses some backward compatible testing
for lists like "a[0]!=undef". Obviously it has the pitfall:
a=[undef, 1,2,3];
But, I can live with it :-), since using is_list() will break even more code
as it hits people using older versions of OpenSCAD who are usally less
experienced programmers.
However, of course you are right: There is no obvious reason to drop the
Boolean data type and introduce old fashioned and error prone semantics in
OpenSCAD.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
This was part of a change to make comparisons more consistent in general.
See my comment here for more explanation:
https://github.com/openscad/openscad/pull/3164#issuecomment-569158586
(Basically try <= or >= operations between 0 / 1 and false / true on
your older versions, and tell me if that makes sense)
Can you provide a self contained example where the change causes a
problem in actual usage?
Boolean types are not "dropped", but there is type coercion which is
now applied consistently between numbers and bools.
is_bool() and is_num() can also be used to determine exact type if needed.
On Sat, Jul 25, 2020 at 9:01 AM Parkinbot <rudolf@digitaldocument.de> wrote:
>
> With 2019.12.21 I get:
>
> echo(true==1); // false
> echo(true==0); // false
>
> this means not more and not less: a boolean is not a number (apples are no
> pears) and you could also write:
>
> echo(true=="1"); // false
> echo(true=="0"); // false
>
> Thus any code testing a condition like "true == x" imposes an implicit type
> check. Changing this behavior can break such checks as well as reveal buggy
> code written under the assumption Booleans are represented as numbers.
> In my libs I have tons of code that uses some backward compatible testing
> for lists like "a[0]!=undef". Obviously it has the pitfall:
>
> a=[undef, 1,2,3];
>
> But, I can live with it :-), since using is_list() will break even more code
> as it hits people using older versions of OpenSCAD who are usally less
> experienced programmers.
>
> However, of course you are right: There is no obvious reason to drop the
> Boolean data type and introduce old fashioned and error prone semantics in
> OpenSCAD.
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
P
Parkinbot
Sat, Jul 25, 2020 5:44 PM
Hans,
with
echo(true>=1); // true
echo(true<=1); // true
it seems consequent to alter the result
echo(true==1); // false -> true
However, I would never try to use
echo(true <= 1); // true
in user code. But if I was to implement this kind of logic, I would simply
always return "false" whenever the operands don't have the same data type.
--
Sent from: http://forum.openscad.org/
Hans,
with
echo(true>=1); // true
echo(true<=1); // true
it seems consequent to alter the result
echo(true==1); // false -> true
However, I would never try to use
echo(true <= 1); // true
in user code. But if I was to implement this kind of logic, I would simply
always return "false" whenever the operands don't have the same data type.
--
Sent from: http://forum.openscad.org/
A
adrianv
Sat, Jul 25, 2020 5:46 PM
It seems that the motivation for the changes was a theoretical sorting
process that needs to sort items of different types. The current dev
version behavior doesn't make much sense and is almost like dropping
booleans and just making "true" a synonym for 1. Except for the fact that
"3" is true. And this is a major inconsistency, not an improvement in
consistency as claimed.
If I search a vector for true entries, e.g. with search([true], [....]) then
the new behavior makes no sense. It gives me entries that are true and
entries that are 1. The two defensible results are entries that are exactly
"true" (as happened in older version) or all entries that would be
interpreted as true in a boolean context, which means all strings, nonempty
lists, nonzero numbers, and so on should be returned. There's no
conceivable reason a user would expect to get back all the "true" and "1"
entries. It's just not a consistent return result. Unless true isn't
really a separate boolean type but is just a synonym for 1.
For relative comparisons it seems like the only reason to compare true to 3
(other than programmer error) is in the sorting context, which was suggested
in the github issue. But does the choice to make true=1 and false=0
actually make sense? I argue that it does not. If I'm sorting a list of
objects I do not find the following to be a reasonable output from a sort
algorithm and can't conceive of a situation where it would be desirable:
[-1, -.5, 0, false, 0, false, .5, 1, true, 1, 1, true, 1.5, 2]
but this would be a legitimate "sorted" output if true==1 and false==0 and
the same for comparisons.
I note that the same dubious logic has not bee applied to strings.
echo(""==0);
echo(""<0);
echo("">0);
all give false in both the july dev and the stable version. Similarly
comparison between lists and scalars also
echo([3]<3);
echo([3]>3);
echo([3]==3);
all give false. So the user who wants to sort a list mixed with lists and
strings and numbers is still on their own to handle comparisons in a
reasonable manner.
I can think of two behaviors that make sense for comparisons between types.
They should either be undef, or there should be a defined order OF TYPES, so
for example:
boolean < number < string < list
In this case the above sort example would presumably sort as
[false,false,true,true,-1, -.5, 0,0, .5, 1, 1, 1, 1.5, 2]
I don't think anybody interested in sorting mixed type lists would be happy
with the previous result, where false and 0 intermix and 1 and true
intermix, so such a user would have to write their own comparison operator
that uses is_bool to check the type to produce a reasonable output---either
with the old behavior or the dev behavior. The updated behavior is
confusing, inconsistent, and hasn't actually helped anything. Is there a
concrete example where the new behavior is actually useful?
So if you want a concrete example, with the updated behavior, search for
booleans becomes difficult to use because outputs from search must be
post-processed to select only the results with boolean output. And the
result without such postprocessing is basically nonsense.
And if I want to search for arrays containing booleans then it's really
painful to screen the return value from search for booleans. Consider:
search([[true,1]], [[true,true],[true,1],[true,3], [1,true], [1,1], [1,3]],
0)
which returns [[1]] in the old version and [[0, 1, 3, 4]] in the new
version. For a general search post-process we now need to scan the returned
entries from search and check whether isbool gives the same result on each
entry to identify which ones are valid returns. What a mess!
I can also say that it's very unexpected to a user that
x==true
will return true when x is equal to 1. And once this is absorbed,
particularly odd that it returns false when x==3. In fact, one could argue
that the only reason a user would ever write "x==true" is to check whether x
is in fact exactly equal to the boolean true and not just a value that gets
cast to true in a boolean context.
thehans wrote
This was part of a change to make comparisons more consistent in general.
See my comment here for more explanation:
https://github.com/openscad/openscad/pull/3164#issuecomment-569158586
(Basically try <= or >= operations between 0 / 1 and false / true on
your older versions, and tell me if that makes sense)
Can you provide a self contained example where the change causes a
problem in actual usage?
Boolean types are not "dropped", but there is type coercion which is
now applied consistently between numbers and bools.
is_bool() and is_num() can also be used to determine exact type if needed.
On Sat, Jul 25, 2020 at 9:01 AM Parkinbot <
With 2019.12.21 I get:
echo(true==1); // false
echo(true==0); // false
this means not more and not less: a boolean is not a number (apples are
no
pears) and you could also write:
echo(true=="1"); // false
echo(true=="0"); // false
Thus any code testing a condition like "true == x" imposes an implicit
type
check. Changing this behavior can break such checks as well as reveal
buggy
code written under the assumption Booleans are represented as numbers.
In my libs I have tons of code that uses some backward compatible testing
for lists like "a[0]!=undef". Obviously it has the pitfall:
a=[undef, 1,2,3];
But, I can live with it :-), since using is_list() will break even more
code
as it hits people using older versions of OpenSCAD who are usally less
experienced programmers.
However, of course you are right: There is no obvious reason to drop the
Boolean data type and introduce old fashioned and error prone semantics
in
OpenSCAD.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
It seems that the motivation for the changes was a theoretical sorting
process that needs to sort items of different types. The current dev
version behavior doesn't make much sense and is almost like dropping
booleans and just making "true" a synonym for 1. Except for the fact that
"3" is true. And this is a major inconsistency, not an improvement in
consistency as claimed.
If I search a vector for true entries, e.g. with search([true], [....]) then
the new behavior makes no sense. It gives me entries that are true and
entries that are 1. The two defensible results are entries that are exactly
"true" (as happened in older version) or all entries that would be
interpreted as true in a boolean context, which means all strings, nonempty
lists, nonzero numbers, and so on should be returned. There's no
conceivable reason a user would expect to get back all the "true" and "1"
entries. It's just not a consistent return result. Unless true isn't
really a separate boolean type but is just a synonym for 1.
For relative comparisons it seems like the only reason to compare true to 3
(other than programmer error) is in the sorting context, which was suggested
in the github issue. But does the choice to make true=1 and false=0
actually make sense? I argue that it does not. If I'm sorting a list of
objects I do not find the following to be a reasonable output from a sort
algorithm and can't conceive of a situation where it would be desirable:
[-1, -.5, 0, false, 0, false, .5, 1, true, 1, 1, true, 1.5, 2]
but this would be a legitimate "sorted" output if true==1 and false==0 and
the same for comparisons.
I note that the same dubious logic has not bee applied to strings.
echo(""==0);
echo(""<0);
echo("">0);
all give false in both the july dev and the stable version. Similarly
comparison between lists and scalars also
echo([3]<3);
echo([3]>3);
echo([3]==3);
all give false. So the user who wants to sort a list mixed with lists and
strings and numbers is still on their own to handle comparisons in a
reasonable manner.
I can think of two behaviors that make sense for comparisons between types.
They should either be undef, or there should be a defined order OF TYPES, so
for example:
boolean < number < string < list
In this case the above sort example would presumably sort as
[false,false,true,true,-1, -.5, 0,0, .5, 1, 1, 1, 1.5, 2]
I don't think anybody interested in sorting mixed type lists would be happy
with the previous result, where false and 0 intermix and 1 and true
intermix, so such a user would have to write their own comparison operator
that uses is_bool to check the type to produce a reasonable output---either
with the old behavior or the dev behavior. The updated behavior is
confusing, inconsistent, and hasn't actually helped anything. Is there a
concrete example where the new behavior is actually useful?
So if you want a concrete example, with the updated behavior, search for
booleans becomes difficult to use because outputs from search must be
post-processed to select only the results with boolean output. And the
result without such postprocessing is basically nonsense.
And if I want to search for arrays containing booleans then it's really
painful to screen the return value from search for booleans. Consider:
search([[true,1]], [[true,true],[true,1],[true,3], [1,true], [1,1], [1,3]],
0)
which returns [[1]] in the old version and [[0, 1, 3, 4]] in the new
version. For a general search post-process we now need to scan the returned
entries from search and check whether isbool gives the same result on each
entry to identify which ones are valid returns. What a mess!
I can also say that it's very unexpected to a user that
x==true
will return true when x is equal to 1. And once this is absorbed,
particularly odd that it returns false when x==3. In fact, one could argue
that the only reason a user would ever write "x==true" is to check whether x
is in fact exactly equal to the boolean true and not just a value that gets
cast to true in a boolean context.
thehans wrote
> This was part of a change to make comparisons more consistent in general.
>
> See my comment here for more explanation:
> https://github.com/openscad/openscad/pull/3164#issuecomment-569158586
> (Basically try <= or >= operations between 0 / 1 and false / true on
> your older versions, and tell me if that makes sense)
>
> Can you provide a self contained example where the change causes a
> problem in actual usage?
>
> Boolean types are not "dropped", but there is type coercion which is
> now applied consistently between numbers and bools.
> is_bool() and is_num() can also be used to determine exact type if needed.
>
>
> On Sat, Jul 25, 2020 at 9:01 AM Parkinbot <
> rudolf@
> > wrote:
>>
>> With 2019.12.21 I get:
>>
>> echo(true==1); // false
>> echo(true==0); // false
>>
>> this means not more and not less: a boolean is not a number (apples are
>> no
>> pears) and you could also write:
>>
>> echo(true=="1"); // false
>> echo(true=="0"); // false
>>
>> Thus any code testing a condition like "true == x" imposes an implicit
>> type
>> check. Changing this behavior can break such checks as well as reveal
>> buggy
>> code written under the assumption Booleans are represented as numbers.
>> In my libs I have tons of code that uses some backward compatible testing
>> for lists like "a[0]!=undef". Obviously it has the pitfall:
>>
>> a=[undef, 1,2,3];
>>
>> But, I can live with it :-), since using is_list() will break even more
>> code
>> as it hits people using older versions of OpenSCAD who are usally less
>> experienced programmers.
>>
>> However, of course you are right: There is no obvious reason to drop the
>> Boolean data type and introduce old fashioned and error prone semantics
>> in
>> OpenSCAD.
>>
>>
>>
>>
>> --
>> Sent from: http://forum.openscad.org/
>>
>> _______________________________________________
>> 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/
HL
Hans L
Sat, Jul 25, 2020 8:42 PM
In cases that expect a boolean such as conditional "if"s, ternary ( ? : ),
or "not" ( ! ) operators, etc, numbers have always been coerced into
bool.
Where 0 (or -0) is false, and all other numbers are true.
This example has same results in any version including 2015.03 :
for (x=[-1,0,1,3,true,false, 1/0, -1/0, 0/0 ]) {
if (x) echo(str(x," is 'true'"));
else echo(str(x," is 'false'"));
if (!x) echo(str("!",x," is 'true'"));
else echo(str("!",x," is 'false'"));
}
echo();
for (x=[-1,0,1,3,true,false, 1/0, -1/0, 0/0 ]) {
echo(str(x," is ", x ? "'true'" : "'false'"));
echo(str("!",x," is ", !x ? "'true'" : "'false'"));
}
So to me, it seems consistent that if: "if(x)" is true, then "if (x==true)"
should also be true.
For relative comparisons it seems like the only reason to compare true to
(other than programmer error) is in the sorting context, which was
in the github issue. But does the choice to make true=1 and false=0
actually make sense? I argue that it does not. If I'm sorting a list of
objects I do not find the following to be a reasonable output from a sort
algorithm and can't conceive of a situation where it would be desirable:
[-1, -.5, 0, false, 0, false, .5, 1, true, 1, 1, true, 1.5, 2]
but this would be a legitimate "sorted" output if true==1 and false==0 and
the same for comparisons.
Yes I did mention this in the context of sorting mixed types, but mainly
just to say that thinking more closely about how mixed types behave was
what led me to discover the inconsistencies in comparison operators.
But I also acknowledged that such sorting would be a weird / rare case, so
I'm not really too upset if it would require special handling for a library
to support such things.
Also note that even though the sorting seems strange here, this is the
exact behavior in Python for example:
Python 3.8.2 (default, Jul 16 2020, 14:00:26)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
x=[-1, -.5, 0, False, 0, False, .5, 1, True, 1, 1, True, 1.5, 2]
y=[-1, -.5, 0, False, 0, False, .5, 1, True, 1, 1, True, 1.5, 2]
x.sort()
x==y
[-1, -0.5, 0, False, 0, False, 0.5, 1, True, 1, 1, True, 1.5, 2]
If this is "old fashioned" as parkinbot says , then I would be curious to
know what modern language(s) which allow mixed type collections, would
serve a better role-model for OpenSCAD in this regard.
So if you want a concrete example, with the updated behavior, search for
booleans becomes difficult to use because outputs from search must be
post-processed to select only the results with boolean output. And the
result without such postprocessing is basically nonsense.
And if I want to search for arrays containing booleans then it's really
painful to screen the return value from search for booleans. Consider:
search([[true,1]], [[true,true],[true,1],[true,3], [1,true], [1,1],
which returns [[1]] in the old version and [[0, 1, 3, 4]] in the new
version. For a general search post-process we now need to scan the
entries from search and check whether isbool gives the same result on each
entry to identify which ones are valid returns. What a mess!
OK, so far I'm hearing that the main thing this actually breaks is certain
usages of the builtin search() function, where the user is searching on
"keys" of mixed boolean and number types. Although WHY they would want to
do this in a real script is not clear to me (outside of adversarially
contrived test-cases).
If this is really wanted/needed for builtin search, then a quick fix could
be to change search() so that it checks for strictly matching types in
addition to "equivalence".
Hans
In cases that expect a boolean such as conditional "if"s, ternary ( ? : ),
or "not" ( ! ) operators, etc, numbers have *always* been coerced into
bool.
Where 0 (or -0) is false, and all other numbers are true.
This example has same results in any version including 2015.03 :
for (x=[-1,0,1,3,true,false, 1/0, -1/0, 0/0 ]) {
if (x) echo(str(x," is 'true'"));
else echo(str(x," is 'false'"));
if (!x) echo(str("!",x," is 'true'"));
else echo(str("!",x," is 'false'"));
}
echo();
for (x=[-1,0,1,3,true,false, 1/0, -1/0, 0/0 ]) {
echo(str(x," is ", x ? "'true'" : "'false'"));
echo(str("!",x," is ", !x ? "'true'" : "'false'"));
}
So to me, it seems consistent that if: "if(x)" is true, then "if (x==true)"
should also be true.
> For relative comparisons it seems like the only reason to compare true to
3
> (other than programmer error) is in the sorting context, which was
suggested
> in the github issue. But does the choice to make true=1 and false=0
> actually make sense? I argue that it does not. If I'm sorting a list of
> objects I do not find the following to be a reasonable output from a sort
> algorithm and can't conceive of a situation where it would be desirable:
>
> [-1, -.5, 0, false, 0, false, .5, 1, true, 1, 1, true, 1.5, 2]
>
> but this would be a legitimate "sorted" output if true==1 and false==0 and
> the same for comparisons.
>
Yes I did mention this in the context of sorting mixed types, but mainly
just to say that thinking more closely about how mixed types behave was
what led me to discover the inconsistencies in comparison operators.
But I also acknowledged that such sorting would be a weird / rare case, so
I'm not really too upset if it would require special handling for a library
to support such things.
Also note that even though the sorting seems strange here, this is the
exact behavior in Python for example:
Python 3.8.2 (default, Jul 16 2020, 14:00:26)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x=[-1, -.5, 0, False, 0, False, .5, 1, True, 1, 1, True, 1.5, 2]
>>> y=[-1, -.5, 0, False, 0, False, .5, 1, True, 1, 1, True, 1.5, 2]
>>> x.sort()
>>> x==y
True
>>> x
[-1, -0.5, 0, False, 0, False, 0.5, 1, True, 1, 1, True, 1.5, 2]
If this is "old fashioned" as parkinbot says , then I would be curious to
know what modern language(s) which allow mixed type collections, would
serve a better role-model for OpenSCAD in this regard.
> So if you want a concrete example, with the updated behavior, search for
> booleans becomes difficult to use because outputs from search must be
> post-processed to select only the results with boolean output. And the
> result without such postprocessing is basically nonsense.
>
> And if I want to search for arrays containing booleans then it's really
> painful to screen the return value from search for booleans. Consider:
>
> search([[true,1]], [[true,true],[true,1],[true,3], [1,true], [1,1],
[1,3]],
> 0)
>
> which returns [[1]] in the old version and [[0, 1, 3, 4]] in the new
> version. For a general search post-process we now need to scan the
returned
> entries from search and check whether isbool gives the same result on each
> entry to identify which ones are valid returns. What a mess!
OK, so far I'm hearing that the main thing this actually breaks is certain
usages of the builtin search() function, where the user is searching on
"keys" of mixed boolean and number types. Although WHY they would want to
do this in a real script is not clear to me (outside of adversarially
contrived test-cases).
If this is really wanted/needed for builtin search, then a quick fix could
be to change search() so that it checks for strictly matching types in
addition to "equivalence".
Hans
A
adrianv
Sun, Jul 26, 2020 12:07 AM
In cases that expect a boolean such as conditional "if"s, ternary ( ? : ),
or "not" ( ! ) operators, etc, numbers have always been coerced into
bool.
Where 0 (or -0) is false, and all other numbers are true.
So to me, it seems consistent that if: "if(x)" is true, then "if
(x==true)"
should also be true.
This doesn't follow. You are arguing that because 1 becomes true in a
boolean context that therefore true must be one in an integer context.
There is no reason this is true. And actually there's definitely no
reason to expect any kind of typecasting to occur in an expression "if
(x==true)". Why would the type of x get changed? I expect x to be compared
as it is to "true" and the result to be false unless they are the same. The
same type and the same value. I don't think an equality test should ever
change the type of something.
Also the argument above means that you need " if (5==true)" to also evaluate
to true. As I said before, for consistency here, true should test as equal
to all nonzeros, nonempty strings and nonempty lists. "if (3)" is true so
"if (3==true)" should be true. "if ([false])" is true so "if
([false]==true)" should be true.
For relative comparisons it seems like the only reason to compare true to
(other than programmer error) is in the sorting context, which was
in the github issue. But does the choice to make true=1 and false=0
actually make sense? I argue that it does not. If I'm sorting a list of
objects I do not find the following to be a reasonable output from a sort
algorithm and can't conceive of a situation where it would be desirable:
[-1, -.5, 0, false, 0, false, .5, 1, true, 1, 1, true, 1.5, 2]
but this would be a legitimate "sorted" output if true==1 and false==0
and
the same for comparisons.
Yes I did mention this in the context of sorting mixed types, but mainly
just to say that thinking more closely about how mixed types behave was
what led me to discover the inconsistencies in comparison operators.
But I also acknowledged that such sorting would be a weird / rare case, so
I'm not really too upset if it would require special handling for a
library
to support such things.
Also note that even though the sorting seems strange here, this is the
exact behavior in Python for example:
Python 3.8.2 (default, Jul 16 2020, 14:00:26)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
x=[-1, -.5, 0, False, 0, False, .5, 1, True, 1, 1, True, 1.5, 2]
y=[-1, -.5, 0, False, 0, False, .5, 1, True, 1, 1, True, 1.5, 2]
x.sort()
x==y
[-1, -0.5, 0, False, 0, False, 0.5, 1, True, 1, 1, True, 1.5, 2]
Indeed. This is because in Python, True and False are in fact integers.
The boolean type is a subclass of the integer type, so this behavior is an
unavoidable consequence of that. This also means that the statement
(x==True) in python doesn't require a typecast if x is 1 because both types
are integers already. As someone said earlier, the decision to make true
and false equal to 1 and 0 is basically getting rid of the boolean type and
making booleans into integers. If that's what you want to do, shouldn't you
make it fully consistent the way it is in Python? Namely that true+true=2,
true*false=0, etc? And indexing works, so you can do foo[true]? Which
might honestly be useful---probably THE only useful thing that comes out of
booleans actually being integers.
If you define true and false to be integers then everything becomes
consistent and also well defined. OpenSCAD is a long way from this, because
most integer operations fail on booleans. Also, why do this? I assume it
was done in Python because python didn't have a boolean type until version
2.3, and then they wanted to make it backward compatible with how people did
things before. I don't think it makes sense to copy a feature of a
language that's basically a historical artifact of that language's
development path.
If this is "old fashioned" as parkinbot says , then I would be curious to
know what modern language(s) which allow mixed type collections, would
serve a better role-model for OpenSCAD in this regard.
Probably any strongly type language that has a real boolean type would be a
better role model. Before the boolean type existed people did comparisons
using numbers. But making the boolean type act half like an integer and
half not isn't old fashioned. It's just inconsistent and confusing. It
doesn't make sense, so users will be surprised. And there's no
justification for the current inconsistent behavior that explains why
booleans are integers when you wish they wouldn't be but not when you might
want them to be.
So if you want a concrete example, with the updated behavior, search for
booleans becomes difficult to use because outputs from search must be
post-processed to select only the results with boolean output. And the
result without such postprocessing is basically nonsense.
And if I want to search for arrays containing booleans then it's really
painful to screen the return value from search for booleans. Consider:
search([[true,1]], [[true,true],[true,1],[true,3], [1,true], [1,1],
which returns [[1]] in the old version and [[0, 1, 3, 4]] in the new
version. For a general search post-process we now need to scan the
entries from search and check whether isbool gives the same result on
each
entry to identify which ones are valid returns. What a mess!
OK, so far I'm hearing that the main thing this actually breaks is certain
usages of the builtin search() function, where the user is searching on
"keys" of mixed boolean and number types. Although WHY they would want to
do this in a real script is not clear to me (outside of adversarially
contrived test-cases).
If this is really wanted/needed for builtin search, then a quick fix could
be to change search() so that it checks for strictly matching types in
addition to "equivalence".
No, any use of booleans breaks with search, not just complicated ones.
It's just that the workaround is more awkward when it's a complicated
search.
Here's the simple case. What if I want to find the first true entry, which
does not seem like a contrived operation.
Old way:
final_result = search([true], data, 1);
New way:
results = search([true], data, 0);
true_results = [for (entry=results[0]) if (is_bool(data[entry])) entry];
final_result2 = len(true_results)>0 ? [true_results[1]] : [[]];
So yeah, if you insist on keeping the new, inconsistent and confusing
behavior for booleans, then I changing search to reproduce the old behavior
seems advisable. Would be nice to have a real equality operator while
you're at it that gives the true result of comparisons without unexpected
typecasting.
What I'm really puzzled about is that this change seems to break things, and
it is counter-intuitive, and it offers NO BENEFIT. Where is your example of
the advantage of this version. As I see it:
Disadvantages of the new approach:
- Inconsistent behavior where true/false are sort of treated as integers
like in Python, but only sometimes.
- Treating booleans as integers is not required or necessarily the best way
to do things. OpenSCAD is not Python.
- Counterintuitive results like "true==1" evaluates as true. (Guess this
makes sense to Python users, but it does not make sense if booleans are
their own type.)
- To test that x is true must write "x && is_bool(x)", decidedly
non-obvious.
- Makes search() hard to use with boolean inputs
Advantages of new approach:
- Consistency in treatment of booleans as integers in relative comparison
with integers
This isn't a real advantage. Can you give any example of a real advantage,
where one can write better code with this? When would I want to use
relative comparisons with booleans at all??? The argument for consistency
is basically taking the useful case of equality comparison and breaking it
to support consistency for the non-useful case of relative comparisons.
The right way to address the consistency problem is to ban relative
comparisons between types, that is, a<b is undef if the operands are
different types. In a strongly typed language this is the expected
outcome.
But if you want to make booleans act like integers in the name of
consistency then you should get rid of the boolean type and just make them
integers, with true and false simply synonyms for 1 and 0. This simplifies
the language a bit and then nobody is surprised by behavior once this
underlying definition is understood.
--
Sent from: http://forum.openscad.org/
thehans wrote
> In cases that expect a boolean such as conditional "if"s, ternary ( ? : ),
> or "not" ( ! ) operators, etc, numbers have *always* been coerced into
> bool.
> Where 0 (or -0) is false, and all other numbers are true.
>
>
> So to me, it seems consistent that if: "if(x)" is true, then "if
> (x==true)"
> should also be true.
This doesn't follow. You are arguing that because 1 becomes true in a
boolean context that therefore true must be one in an integer context.
There is no reason this is true. And actually there's *definitely* no
reason to expect any kind of typecasting to occur in an expression "if
(x==true)". Why would the type of x get changed? I expect x to be compared
as it is to "true" and the result to be false unless they are the same. The
same type and the same value. I don't think an equality test should *ever*
change the type of something.
Also the argument above means that you need " if (5==true)" to also evaluate
to true. As I said before, for consistency here, true should test as equal
to all nonzeros, nonempty strings and nonempty lists. "if (3)" is true so
"if (3==true)" should be true. "if ([false])" is true so "if
([false]==true)" should be true.
>> For relative comparisons it seems like the only reason to compare true to
> 3
>> (other than programmer error) is in the sorting context, which was
> suggested
>> in the github issue. But does the choice to make true=1 and false=0
>> actually make sense? I argue that it does not. If I'm sorting a list of
>> objects I do not find the following to be a reasonable output from a sort
>> algorithm and can't conceive of a situation where it would be desirable:
>>
>> [-1, -.5, 0, false, 0, false, .5, 1, true, 1, 1, true, 1.5, 2]
>>
>> but this would be a legitimate "sorted" output if true==1 and false==0
>> and
>> the same for comparisons.
>>
>
> Yes I did mention this in the context of sorting mixed types, but mainly
> just to say that thinking more closely about how mixed types behave was
> what led me to discover the inconsistencies in comparison operators.
> But I also acknowledged that such sorting would be a weird / rare case, so
> I'm not really too upset if it would require special handling for a
> library
> to support such things.
>
> Also note that even though the sorting seems strange here, this is the
> exact behavior in Python for example:
> Python 3.8.2 (default, Jul 16 2020, 14:00:26)
> [GCC 9.3.0] on linux
> Type "help", "copyright", "credits" or "license" for more information.
>>>> x=[-1, -.5, 0, False, 0, False, .5, 1, True, 1, 1, True, 1.5, 2]
>>>> y=[-1, -.5, 0, False, 0, False, .5, 1, True, 1, 1, True, 1.5, 2]
>>>> x.sort()
>>>> x==y
> True
>>>> x
> [-1, -0.5, 0, False, 0, False, 0.5, 1, True, 1, 1, True, 1.5, 2]
Indeed. This is because in Python, True and False are in fact integers.
The boolean type is a subclass of the integer type, so this behavior is an
unavoidable consequence of that. This also means that the statement
(x==True) in python doesn't require a typecast if x is 1 because both types
are integers already. As someone said earlier, the decision to make true
and false equal to 1 and 0 is basically getting rid of the boolean type and
making booleans into integers. If that's what you want to do, shouldn't you
make it fully consistent the way it is in Python? Namely that true+true=2,
true*false=0, etc? And indexing works, so you can do foo[true]? Which
might honestly be useful---probably THE only useful thing that comes out of
booleans actually being integers.
If you define true and false to be integers then everything becomes
consistent and also well defined. OpenSCAD is a long way from this, because
most integer operations fail on booleans. Also, why do this? I assume it
was done in Python because python didn't have a boolean type until version
2.3, and then they wanted to make it backward compatible with how people did
things before. I don't think it makes sense to copy a feature of a
language that's basically a historical artifact of that language's
development path.
> If this is "old fashioned" as parkinbot says , then I would be curious to
> know what modern language(s) which allow mixed type collections, would
> serve a better role-model for OpenSCAD in this regard.
Probably any strongly type language that has a real boolean type would be a
better role model. Before the boolean type existed people did comparisons
using numbers. But making the boolean type act half like an integer and
half not isn't old fashioned. It's just inconsistent and confusing. It
doesn't make sense, so users will be surprised. And there's no
justification for the current inconsistent behavior that explains why
booleans are integers when you wish they wouldn't be but not when you might
want them to be.
>> So if you want a concrete example, with the updated behavior, search for
>> booleans becomes difficult to use because outputs from search must be
>> post-processed to select only the results with boolean output. And the
>> result without such postprocessing is basically nonsense.
>>
>> And if I want to search for arrays containing booleans then it's really
>> painful to screen the return value from search for booleans. Consider:
>>
>> search([[true,1]], [[true,true],[true,1],[true,3], [1,true], [1,1],
> [1,3]],
>> 0)
>>
>> which returns [[1]] in the old version and [[0, 1, 3, 4]] in the new
>> version. For a general search post-process we now need to scan the
> returned
>> entries from search and check whether isbool gives the same result on
>> each
>> entry to identify which ones are valid returns. What a mess!
>
> OK, so far I'm hearing that the main thing this actually breaks is certain
> usages of the builtin search() function, where the user is searching on
> "keys" of mixed boolean and number types. Although WHY they would want to
> do this in a real script is not clear to me (outside of adversarially
> contrived test-cases).
> If this is really wanted/needed for builtin search, then a quick fix could
> be to change search() so that it checks for strictly matching types in
> addition to "equivalence".
No, *any* use of booleans breaks with search, not just complicated ones.
It's just that the workaround is more awkward when it's a complicated
search.
Here's the simple case. What if I want to find the first true entry, which
does not seem like a contrived operation.
Old way:
final_result = search([true], data, 1);
New way:
results = search([true], data, 0);
true_results = [for (entry=results[0]) if (is_bool(data[entry])) entry];
final_result2 = len(true_results)>0 ? [true_results[1]] : [[]];
So yeah, if you insist on keeping the new, inconsistent and confusing
behavior for booleans, then I changing search to reproduce the old behavior
seems advisable. Would be nice to have a real equality operator while
you're at it that gives the true result of comparisons without unexpected
typecasting.
What I'm really puzzled about is that this change seems to break things, and
it is counter-intuitive, and it offers NO BENEFIT. Where is your example of
the advantage of this version. As I see it:
Disadvantages of the new approach:
* Inconsistent behavior where true/false are sort of treated as integers
like in Python, but only sometimes.
* Treating booleans as integers is not required or necessarily the best way
to do things. OpenSCAD is not Python.
* Counterintuitive results like "true==1" evaluates as true. (Guess this
makes sense to Python users, but it does not make sense if booleans are
their own type.)
* To test that x is true must write "x && is_bool(x)", decidedly
non-obvious.
* Makes search() hard to use with boolean inputs
Advantages of new approach:
* Consistency in treatment of booleans as integers in relative comparison
with integers
This isn't a real advantage. Can you give any example of a real advantage,
where one can write better code with this? When would I want to use
relative comparisons with booleans at all??? The argument for consistency
is basically taking the useful case of equality comparison and breaking it
to support consistency for the non-useful case of relative comparisons.
The right way to address the consistency problem is to ban relative
comparisons between types, that is, a<b is undef if the operands are
different types. In a strongly typed language this is the expected
outcome.
But if you want to make booleans act like integers in the name of
consistency then you should get rid of the boolean type and just make them
integers, with true and false simply synonyms for 1 and 0. This simplifies
the language a bit and then nobody is surprised by behavior once this
underlying definition is understood.
--
Sent from: http://forum.openscad.org/
DM
Doug Moen
Sun, Jul 26, 2020 5:22 AM
So to me, it seems consistent that if: "if(x)" is true, then "if (x==true)" should also be true.
OpenSCAD boolean operations treat [] as equivalent to false. For consistency, should [] == false?
OpenSCAD boolean operations treat 3 as equivalent to true. For consistency, should 3 == true?
OpenSCAD arithmetic operations do not treat booleans as equivalent to numbers. 1+true is undef, not 2.
For consistency, shouldn't true != 1?
I think that in general, a==b should return true if a and b are operationally equivalent in all contexts, and false otherwise. Another formulation that I like is: a == b should return true if and only if a and b have the same printed representation. Any exceptions to this general rule should be justified by interoperability concerns. For example, 0 == -0 can be justified to support interoperability with floating point algorithms ported from other programming languages, since the IEEE float standard requires 0 == -0 and almost all other programming languages follow that.
> So to me, it seems consistent that if: "if(x)" is true, then "if (x==true)" should also be true.
OpenSCAD boolean operations treat [] as equivalent to false. For consistency, should [] == false?
OpenSCAD boolean operations treat 3 as equivalent to true. For consistency, should 3 == true?
OpenSCAD arithmetic operations do *not* treat booleans as equivalent to numbers. 1+true is undef, not 2.
For consistency, shouldn't true != 1?
I think that in general, a==b should return true if a and b are operationally equivalent in all contexts, and false otherwise. Another formulation that I like is: a == b should return true if and only if a and b have the same printed representation. Any exceptions to this general rule should be justified by interoperability concerns. For example, 0 == -0 can be justified to support interoperability with floating point algorithms ported from other programming languages, since the IEEE float standard requires 0 == -0 and almost all other programming languages follow that.
HL
Hans L
Sun, Jul 26, 2020 6:44 AM
OK, I see your points and admit that my logic in that statement was
flawed.
I suppose I hadn't put a ton of thought into it and was attempting to
post-rationalize, but upon reflection, really the main reason this behavior
was chosen is that it is consistent with C++.
So it seemed natural to me, developing for OpenSCAD in C++, to treat double
to bool comparisons in the same way (the code literally returns the result
of the corresponding C++ operator between double and bool). And as an
aside, there are no integers in OpenSCAD.
Now everyone can balk that C++ is old fashioned with all its legacy quirks
and OpenSCAD is not Python / C / C++ / Javascript / etc. but I honestly
didn't think anyone would even consider this change controversial... and
there was no input of other opinions at the time (nor in the 7months that
the change has been in nightlies).
I only knew that the existing behavior (where <= and >= behaved C-like, but
not other comparisons) made absolutely no sense, but I suppose the change
could have just as easily gone the other way: to make <= and >= always
return false when comparing between numbers and bools.
If we can agree that's what our users want/expect in general, then
personally I would be fine with changing it around.
As for the more drastic option of trying to make OpenSCAD strongly typed
(which I don't believe anyone has ever claimed it to be), by having
comparisons between any two different types returning undef instead of
false... I have a feeling doing that would cause significantly more
breakages to existing scripts, and general confusion, than the previous
change.
On Sun, Jul 26, 2020 at 12:26 AM Doug Moen doug@moens.org wrote:
So to me, it seems consistent that if: "if(x)" is true, then "if
(x==true)" should also be true.
OpenSCAD boolean operations treat [] as equivalent to false. For
consistency, should [] == false?
OpenSCAD boolean operations treat 3 as equivalent to true. For
consistency, should 3 == true?
OpenSCAD arithmetic operations do not treat booleans as equivalent to
numbers. 1+true is undef, not 2.
For consistency, shouldn't true != 1?
I think that in general, a==b should return true if a and b are
operationally equivalent in all contexts, and false otherwise. Another
formulation that I like is: a == b should return true if and only if a and
b have the same printed representation. Any exceptions to this general rule
should be justified by interoperability concerns. For example, 0 == -0 can
be justified to support interoperability with floating point algorithms
ported from other programming languages, since the IEEE float standard
requires 0 == -0 and almost all other programming languages follow that.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
OK, I see your points and admit that my logic in that statement was
flawed.
I suppose I hadn't put a ton of thought into it and was attempting to
post-rationalize, but upon reflection, really the main reason this behavior
was chosen is that it is consistent **with C++**.
So it seemed natural to me, developing for OpenSCAD in C++, to treat double
to bool comparisons in the same way (the code literally returns the result
of the corresponding C++ operator between double and bool). And as an
aside, there are no integers in OpenSCAD.
Now everyone can balk that C++ is old fashioned with all its legacy quirks
and OpenSCAD is not Python / C / C++ / Javascript / etc. but I honestly
didn't think anyone would even consider this change controversial... and
there was no input of other opinions at the time (nor in the 7months that
the change has been in nightlies).
I only knew that the existing behavior (where <= and >= behaved C-like, but
not other comparisons) made absolutely no sense, but I suppose the change
could have just as easily gone the other way: to make <= and >= always
return false when comparing between numbers and bools.
If we can agree that's what our users want/expect in general, then
personally I would be fine with changing it around.
As for the more drastic option of trying to make OpenSCAD strongly typed
(which I don't believe anyone has ever claimed it to be), by having
comparisons between any two different types returning undef instead of
false... I have a feeling doing that would cause significantly more
breakages to existing scripts, and general confusion, than the previous
change.
On Sun, Jul 26, 2020 at 12:26 AM Doug Moen <doug@moens.org> wrote:
> > So to me, it seems consistent that if: "if(x)" is true, then "if
> (x==true)" should also be true.
>
> OpenSCAD boolean operations treat [] as equivalent to false. For
> consistency, should [] == false?
> OpenSCAD boolean operations treat 3 as equivalent to true. For
> consistency, should 3 == true?
>
> OpenSCAD arithmetic operations do *not* treat booleans as equivalent to
> numbers. 1+true is undef, not 2.
> For consistency, shouldn't true != 1?
>
> I think that in general, a==b should return true if a and b are
> operationally equivalent in all contexts, and false otherwise. Another
> formulation that I like is: a == b should return true if and only if a and
> b have the same printed representation. Any exceptions to this general rule
> should be justified by interoperability concerns. For example, 0 == -0 can
> be justified to support interoperability with floating point algorithms
> ported from other programming languages, since the IEEE float standard
> requires 0 == -0 and almost all other programming languages follow that.
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
P
Parkinbot
Sun, Jul 26, 2020 10:14 AM
As for the more drastic option of trying to make OpenSCAD strongly typed
(which I don't believe anyone has ever claimed it to be), by having
comparisons between any two different types returning undef instead of
false... I have a feeling doing that would cause significantly more
breakages to existing scripts, and general confusion, than the previous
change.
This is not an option for a boolean operation unless you identify "undef" as
"false" and get caught in the same pitfall. You could throw an error or at
least a warning, but this will break tons of legacy code.
I would opt for a clear and consistent implementation, which is neither
the new nor the old implementation.
--
Sent from: http://forum.openscad.org/
thehans wrote
> As for the more drastic option of trying to make OpenSCAD strongly typed
> (which I don't believe anyone has ever claimed it to be), by having
> comparisons between any two different types returning undef instead of
> false... I have a feeling doing that would cause significantly more
> breakages to existing scripts, and general confusion, than the previous
> change.
This is not an option for a boolean operation unless you identify "undef" as
"false" and get caught in the same pitfall. You could throw an error or at
least a warning, but this will break tons of legacy code.
I would opt for a clear and *consistent* implementation, which is neither
the new nor the old implementation.
--
Sent from: http://forum.openscad.org/
RW
Ray West
Sun, Jul 26, 2020 11:55 AM
Hi,
I'm new to scad, this is just an opinion, but I wonder exactly what the
problem is that is being discussed. Looking at
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Type_Test_Functions/is_bool
 the 'is_bool' makes sense to me, i.e the only acceptable values for
bool is 'true' and 'false', any other value is false. Now it maybe that
within the computer memory, a bit set to 0 represents a true, but in
other computer systems, it could be a whole byte set to 0. You can read
bits and bytes and translate to whatever you want, but you have to stick
with that machine/language convention, to get the same output as input.
Going up a level, if a bool is defined as either true or false, then you
shouldn't think of it as anything else, no matter how your favourite
language handles the code. It should be strongly typed, else folk will
want to put there own interpretation on non conforming values. In the
same way, if we are talking about decimal numbers, we would expect that
something like '555bnh' would be handled differently in different
language interpretations of an integer, but '555' should always be
handled the same. An example of this woolly thinking, is found all over
window's programs, where programmers found undocumented short cuts
within the os, outside of the apis, and then when the OS was updated,
their software failed.
Is the next discussion going to be about how many angels can sit on a
pin head?
Best wishes,
Ray
Hi,
I'm new to scad, this is just an opinion, but I wonder exactly what the
problem is that is being discussed. Looking at
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Type_Test_Functions/is_bool
 the 'is_bool' makes sense to me, i.e the only acceptable values for
bool is 'true' and 'false', any other value is false. Now it maybe that
within the computer memory, a bit set to 0 represents a true, but in
other computer systems, it could be a whole byte set to 0. You can read
bits and bytes and translate to whatever you want, but you have to stick
with that machine/language convention, to get the same output as input.
Going up a level, if a bool is defined as either true or false, then you
shouldn't think of it as anything else, no matter how your favourite
language handles the code. It should be strongly typed, else folk will
want to put there own interpretation on non conforming values. In the
same way, if we are talking about decimal numbers, we would expect that
something like '555bnh' would be handled differently in different
language interpretations of an integer, but '555' should always be
handled the same. An example of this woolly thinking, is found all over
window's programs, where programmers found undocumented short cuts
within the os, outside of the apis, and then when the OS was updated,
their software failed.
Is the next discussion going to be about how many angels can sit on a
pin head?
Best wishes,
Ray
J
jon
Sun, Jul 26, 2020 12:26 PM
I grew up on Pascal and Modula-2. In those languages, TRUE/FALSE have
nothing to do with numbers: "IF (3)" is simply an error. So to ME, all
of this is nonsense.
Which demonstrates how differently we all feel about these issues
On 7/26/2020 2:44 AM, Hans L wrote:
OK, I see your points and admit that my logic in that statement was
flawed.
I suppose I hadn't put a ton of thought into it and was attempting to
post-rationalize, but upon reflection, really the main reason this
behavior was chosen is that it is consistent with C++.
So it seemed natural to me, developing for OpenSCAD in C++, to treat
double to bool comparisons in the same way (the code literally returns
the result of the corresponding C++ operator between double and
bool). And as an aside, there are no integers in OpenSCAD.
Now everyone can balk that C++ is old fashioned with all its legacy
quirks and OpenSCAD is not Python / C / C++ / Javascript / etc. but I
honestly didn't think anyone would even consider this change
controversial... and there was no input of other opinions at the time
(nor in the 7months that the change has been in nightlies).
I only knew that the existing behavior (where <= and >= behaved
C-like, but not other comparisons) made absolutely no sense, but I
suppose the change could have just as easily gone the other way:Â to
make <= and >= always return false when comparing between numbers
and bools.
If we can agree that's what our users want/expect in general, then
personally I would be fine with changing it around.
As for the more drastic option of trying to make OpenSCAD strongly
typed (which I don't believe anyone has ever claimed it to be), by
having comparisons between any two different types returning undef
instead of false... I have a feeling doing that would cause
significantly more breakages to existing scripts, and general
confusion, than the previous change.
On Sun, Jul 26, 2020 at 12:26 AM Doug Moen <doug@moens.org
mailto:doug@moens.org> wrote:
So to me, it seems consistent that if: "if(x)" is true, then "if
(x==true)" should also be true.
OpenSCAD boolean operations treat [] as equivalent to false. For
consistency, should [] == false?
OpenSCAD boolean operations treat 3 as equivalent to true. For
consistency, should 3 == true?
OpenSCAD arithmetic operations do *not* treat booleans as
equivalent to numbers. 1+true is undef, not 2.
For consistency, shouldn't true != 1?
I think that in general, a==b should return true if a and b are
operationally equivalent in all contexts, and false otherwise.
Another formulation that I like is: a == b should return true if
and only if a and b have the same printed representation. Any
exceptions to this general rule should be justified by
interoperability concerns. For example, 0 == -0 can be justified
to support interoperability with floating point algorithms ported
from other programming languages, since the IEEE float standard
requires 0 == -0 and almost all other programming languages follow
that.
_______________________________________________
OpenSCAD mailing list
Discuss@lists.openscad.org <mailto:Discuss@lists.openscad.org>
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
I grew up on Pascal and Modula-2. In those languages, TRUE/FALSE have
nothing to do with numbers: "IF (3)" is simply an error. So to ME, all
of this is nonsense.
Which demonstrates how differently we all feel about these issues
On 7/26/2020 2:44 AM, Hans L wrote:
> OK, I see your points and admit that my logic in that statement was
> flawed.
>
> I suppose I hadn't put a ton of thought into it and was attempting to
> post-rationalize, but upon reflection, really the main reason this
> behavior was chosen is that it is consistent **with C++**.
> So it seemed natural to me, developing for OpenSCAD in C++, to treat
> double to bool comparisons in the same way (the code literally returns
> the result of the corresponding C++ operator between double and
> bool). And as an aside, there are no integers in OpenSCAD.
>
> Now everyone can balk that C++ is old fashioned with all its legacy
> quirks and OpenSCAD is not Python / C / C++ / Javascript / etc. but I
> honestly didn't think anyone would even consider this change
> controversial... and there was no input of other opinions at the time
> (nor in the 7months that the change has been in nightlies).
>
> I only knew that the existing behavior (where <= and >= behaved
> C-like, but not other comparisons) made absolutely no sense, but I
> suppose the change could have just as easily gone the other way:Â to
> make <= and >= always return false when comparing between numbers
> and bools.
>
> If we can agree that's what our users want/expect in general, then
> personally I would be fine with changing it around.
>
> As for the more drastic option of trying to make OpenSCAD strongly
> typed (which I don't believe anyone has ever claimed it to be), by
> having comparisons between any two different types returning undef
> instead of false... I have a feeling doing that would cause
> significantly more breakages to existing scripts, and general
> confusion, than the previous change.
>
>
>
> On Sun, Jul 26, 2020 at 12:26 AM Doug Moen <doug@moens.org
> <mailto:doug@moens.org>> wrote:
>
> > So to me, it seems consistent that if: "if(x)" is true, then "if
> (x==true)" should also be true.
>
> OpenSCAD boolean operations treat [] as equivalent to false. For
> consistency, should [] == false?
> OpenSCAD boolean operations treat 3 as equivalent to true. For
> consistency, should 3 == true?
>
> OpenSCAD arithmetic operations do *not* treat booleans as
> equivalent to numbers. 1+true is undef, not 2.
> For consistency, shouldn't true != 1?
>
> I think that in general, a==b should return true if a and b are
> operationally equivalent in all contexts, and false otherwise.
> Another formulation that I like is: a == b should return true if
> and only if a and b have the same printed representation. Any
> exceptions to this general rule should be justified by
> interoperability concerns. For example, 0 == -0 can be justified
> to support interoperability with floating point algorithms ported
> from other programming languages, since the IEEE float standard
> requires 0 == -0 and almost all other programming languages follow
> that.
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org <mailto:Discuss@lists.openscad.org>
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
M
MichaelAtOz
Sun, Jul 26, 2020 12:33 PM
I only knew that the existing behavior (where <= and >= behaved C-like,
but
not other comparisons) made absolutely no sense, but I suppose the change
could have just as easily gone the other way: to make <= and >= always
return false when comparing between numbers and bools.
If we can agree that's what our users want/expect in general, then
personally I would be fine with changing it around.
Booleans are truth values. There are two Boolean values, namely true and
false. A Boolean is passed as the argument to conditional statement
'if()'. conditional operator '? :', and logical operators '!' (not), '&&'
(and), and '||' (or). In all of these contexts, you can actually pass any
quantity. Most values are converted to 'true' in a Boolean context, the
values that count as 'false' are:
false
0 and -0
""
[]
undef
Note that "false" (the string), [0] (a numeric vector), [ [] ] (a vector
containing an empty vector), [false] (a vector containing the Boolean
value false) and 0/0 (Not A Number) all count as true.
So much code is based on that. Why change?
Admin - email* me if you need anything, or if I've done something stupid...
- click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
thehans wrote
> I only knew that the existing behavior (where <= and >= behaved C-like,
> but
> not other comparisons) made absolutely no sense, but I suppose the change
> could have just as easily gone the other way: to make <= and >= always
> return false when comparing between numbers and bools.
>
> If we can agree that's what our users want/expect in general, then
> personally I would be fine with changing it around.
wiki
> Booleans are truth values. There are two Boolean values, namely true and
> false. A Boolean is passed as the argument to conditional statement
> 'if()'. conditional operator '? :', and logical operators '!' (not), '&&'
> (and), and '||' (or). In all of these contexts, you can actually pass any
> quantity. Most values are converted to 'true' in a Boolean context, the
> values that count as 'false' are:
>
> false
> 0 and -0
> ""
> []
> undef
>
> Note that "false" (the string), [0] (a numeric vector), [ [] ] (a vector
> containing an empty vector), [false] (a vector containing the Boolean
> value false) and 0/0 (Not A Number) all count as true.
So much code is based on that. Why change?
-----
Admin - email* me if you need anything, or if I've done something stupid...
* click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
A
adrianv
Sun, Jul 26, 2020 12:50 PM
Note that the change under discussion may have been made a while ago, but I,
at least, don't run the nightly builds because I'm doing library development
and the library is meant to run under the stable release.
In what way(s) is OpenSCAD not strongly typed? Generally when I combine
types in an operation OpenSCAD returns undef. This is why I suggested
OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
issuing an error.
In what way would you say that OpenSCAD is not strongly type? I have come
up with one example of weak typing: the automatic casting of non-booleans to
booleans in a boolean context. Are there any others?
test = [
"string",
[3],
33,
true];
result = [for(i=[0:1:3], j=[i+1:1:3]) test[i]+test[j]];
echo(result);
Testing for all types above, I get undef for every case. In what way would
you say that OpenSCAD is not strongly type? I have come up with one example
of weak typing: the automatic casting of non-booleans to booleans in a
boolean context. Are there any others?
In fact, the various weak-type behaviors I'd like to see are all absent.
I'm talking about things like array+scalar, or comparisons like array<scalar
or array<array. Note: I'm not requesting these things or saying we need
them. I'm just saying they are weak type behaviors I'm used to from MATLAB
where OpenSCAD just returns undef--the strong type behavior.
The only operations that I can find that doesn't return undef or return an
appropriate multi-type result (e.g. matrix times scalar) are the relative
comparisons. If you really want to make things consistent it still seems to
me that the best way to do that is to make relative operations return undef
for mismatched types.
I don't understand how this change would break code that is not also broken
by changing comparisons of booleans to return false.
Parkinbot, I don't understand what you mean. Right now undef is treated as
false in a boolean context, and nobody is suggesting that this change. So
someone who does comparisons like "if (true<3)" will get the
same result if such comparisons return false as if they return undef. The
only difference would be if you store the result or explicitly check the
result like
(true<3) == undef
or
(true<3) == false
<quote author="Parkinbot">
thehans wrote
As for the more drastic option of trying to make OpenSCAD strongly typed
(which I don't believe anyone has ever claimed it to be), by having
comparisons between any two different types returning undef instead of
false... I have a feeling doing that would cause significantly more
breakages to existing scripts, and general confusion, than the previous
change.
Note that the change under discussion may have been made a while ago, but I,
at least, don't run the nightly builds because I'm doing library development
and the library is meant to run under the stable release.
In what way(s) is OpenSCAD not strongly typed? Generally when I combine
types in an operation OpenSCAD returns undef. This is why I suggested
OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
issuing an error.
In what way would you say that OpenSCAD is not strongly type? I have come
up with one example of weak typing: the automatic casting of non-booleans to
booleans in a boolean context. Are there any others?
test = [
"string",
[3],
33,
true];
result = [for(i=[0:1:3], j=[i+1:1:3]) test[i]+test[j]];
echo(result);
Testing for all types above, I get undef for every case. In what way would
you say that OpenSCAD is not strongly type? I have come up with one example
of weak typing: the automatic casting of non-booleans to booleans in a
boolean context. Are there any others?
In fact, the various weak-type behaviors I'd like to see are all absent.
I'm talking about things like array+scalar, or comparisons like array<scalar
or array<array. Note: I'm not requesting these things or saying we need
them. I'm just saying they are weak type behaviors I'm used to from MATLAB
where OpenSCAD just returns undef--the strong type behavior.
The only operations that I can find that doesn't return undef or return an
appropriate multi-type result (e.g. matrix times scalar) are the relative
comparisons. If you really want to make things consistent it still seems to
me that the best way to do that is to make relative operations return undef
for mismatched types.
I don't understand how this change would break code that is not also broken
by changing comparisons of booleans to return false.
Parkinbot, I don't understand what you mean. Right now undef is treated as
false in a boolean context, and nobody is suggesting that this change. So
someone who does comparisons like "if (true<3)" will get the
same result if such comparisons return false as if they return undef. The
only difference would be if you store the result or explicitly check the
result like
(true<3) == undef
or
(true<3) == false
<quote author="Parkinbot">
thehans wrote
> As for the more drastic option of trying to make OpenSCAD strongly typed
> (which I don't believe anyone has ever claimed it to be), by having
> comparisons between any two different types returning undef instead of
> false... I have a feeling doing that would cause significantly more
> breakages to existing scripts, and general confusion, than the previous
> change.
This is not an option for a boolean operation unless you identify "undef" as
"false" and get caught in the same pitfall. You could throw an error or at
least a warning, but this will break tons of legacy code.
I would opt for a clear and *consistent* implementation, which is neither
the new nor the old implementation.
--
Sent from: http://forum.openscad.org/
_______________________________________________
OpenSCAD mailing list
Discuss@.openscad
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
Sent from: http://forum.openscad.org/
A
adrianv
Sun, Jul 26, 2020 1:35 PM
I only knew that the existing behavior (where <= and >= behaved C-like,
but
not other comparisons) made absolutely no sense, but I suppose the change
could have just as easily gone the other way: to make <= and >= always
return false when comparing between numbers and bools.
If we can agree that's what our users want/expect in general, then
personally I would be fine with changing it around.
Booleans are truth values. There are two Boolean values, namely true and
false. A Boolean is passed as the argument to conditional statement
'if()'. conditional operator '? :', and logical operators '!' (not), '&&'
(and), and '||' (or). In all of these contexts, you can actually pass any
quantity. Most values are converted to 'true' in a Boolean context, the
values that count as 'false' are:
false
0 and -0
""
[]
undef
Note that "false" (the string), [0] (a numeric vector), [ [] ] (a vector
containing an empty vector), [false] (a vector containing the Boolean
value false) and 0/0 (Not A Number) all count as true.
So much code is based on that. Why change?
This doesn't relate to the current conversation about the behavior of
booleans in relative comparisons. Nobody is suggesting that we change how
boolean contexts are handled, with the argument in such a context being cast
to a boolean following the above method.
The question at hand is what should happen when you use the <, >, <=, =>, or
== operators with booleans. Is there tons of code based on using relative
comparisons between numbers and booleans? What is the use of such a thing?
It doesn't make sense. Only equality testing makes sense, and the whole
issue at hand is that the latest development breaks equality testing.
The wiki says this:
If both operands are Booleans, true > false. In an inequality comparison
between a Boolean and a number true is treated as 1 and false is treated as
0. Other inequality tests involving Booleans return false.
Dissimilar types always test as unequal with '==' and '!='. Inequality
comparisons between dissimilar types, except for Boolean and numbers as
noted above, always result in false.
So the question is what should be behavior be for inequality tests between
dissimilar types. I'm suggesting that it should be to return undef, since
that is consistent with how other operations behave when you combine
dissimilar types. And this should rarely break anything since undef casts
to false in a boolean context. In other words, changing 3<[3] from false to
undef won't change code that uses this in a boolean context, since undef is
false in that context. The only real change would be to code that relies on
comparing booleans to numbers because the change would mean that true<4
would return undef instead of true, and that would test as false. But if
we went with the other notion of returning false for this test it has the
same impact on existing code.
--
Sent from: http://forum.openscad.org/
MichaelAtOz wrote
> thehans wrote
>> I only knew that the existing behavior (where <= and >= behaved C-like,
>> but
>> not other comparisons) made absolutely no sense, but I suppose the change
>> could have just as easily gone the other way: to make <= and >= always
>> return false when comparing between numbers and bools.
>>
>> If we can agree that's what our users want/expect in general, then
>> personally I would be fine with changing it around.
>
> wiki
>
>> Booleans are truth values. There are two Boolean values, namely true and
>> false. A Boolean is passed as the argument to conditional statement
>> 'if()'. conditional operator '? :', and logical operators '!' (not), '&&'
>> (and), and '||' (or). In all of these contexts, you can actually pass any
>> quantity. Most values are converted to 'true' in a Boolean context, the
>> values that count as 'false' are:
>>
>> false
>> 0 and -0
>> ""
>> []
>> undef
>>
>> Note that "false" (the string), [0] (a numeric vector), [ [] ] (a vector
>> containing an empty vector), [false] (a vector containing the Boolean
>> value false) and 0/0 (Not A Number) all count as true.
>
> So much code is based on that. Why change?
This doesn't relate to the current conversation about the behavior of
booleans in relative comparisons. Nobody is suggesting that we change how
boolean contexts are handled, with the argument in such a context being cast
to a boolean following the above method.
The question at hand is what should happen when you use the <, >, <=, =>, or
== operators with booleans. Is there tons of code based on using relative
comparisons between numbers and booleans? What is the use of such a thing?
It doesn't make sense. Only equality testing makes sense, and the whole
issue at hand is that the latest development breaks equality testing.
The wiki says this:
---------
If both operands are Booleans, true > false. In an inequality comparison
between a Boolean and a number true is treated as 1 and false is treated as
0. Other inequality tests involving Booleans return false.
Dissimilar types always test as unequal with '==' and '!='. Inequality
comparisons between dissimilar types, except for Boolean and numbers as
noted above, always result in false.
--------
So the question is what should be behavior be for inequality tests between
dissimilar types. I'm suggesting that it should be to return undef, since
that is consistent with how other operations behave when you combine
dissimilar types. And this should rarely break anything since undef casts
to false in a boolean context. In other words, changing 3<[3] from false to
undef won't change code that uses this in a boolean context, since undef is
false in that context. The only real change would be to code that relies on
comparing booleans to numbers because the change would mean that true<4
would return undef instead of true, and that would test as false. But if
we went with the other notion of returning false for this test it has the
same impact on existing code.
--
Sent from: http://forum.openscad.org/
HL
Hans L
Sun, Jul 26, 2020 7:59 PM
I would opt for a clear and consistent implementation, which is neither
the new nor the old implementation.
OK, but saying this alone is not helpful without explicitly defining what
consistent means to you.
So, would you be satisfied with what I proposed here (yes/no)? :
On Sun, Jul 26, 2020 at 1:44 AM Hans L thehans@gmail.com wrote:
... the change could have just as easily gone the other way: to make <=
and >= always return false when comparing between numbers and bools.
If we can agree that's what our users want/expect in general, then
personally I would be fine with changing it around.
Note that the change under discussion may have been made a while ago, but
I,
at least, don't run the nightly builds because I'm doing library
development
and the library is meant to run under the stable release.
I can certainly understand the desire to support stable releases for
libraries (i.e. not relying on experimental features). But as a library
developer it seems to me you also have a vested interest in future
development of OpenSCAD.
So while you may prefer to develop for (and on) the stable release, I would
strongly encourage you and other library authors to, at least periodically,
test against nightlies. This would really help identify any possible
breaking changes early on, before they become more permanently ingrained in
the next stable release.
OpenSCAD has a fairly comprehensive test suite which helps identify many
such regressions, but it's thoroughness is dependent on issues being raised
on Github with reproducible test cases etc.
Also, as a library author, I would assume you are more likely to make use
of esoteric / niche features of the language which more casual users may
not be aware of, which makes them even more valuable as test cases.
In what way(s) is OpenSCAD not strongly typed?
I don't care to dive deeply into semantic arguments here, since it seems
that strong typing means different things, with various qualifications and
exceptions, depending on who/what language you ask.
What I specifically said was "... I don't believe anyone has ever claimed
[OpenSCAD] to be [strongly typed]" (with the exception of you, now). This
means, to the best of my knowledge, none of the documentation nor project
authors have made this claim.
If you want to argue against that, then the burden of proof is on you to
show otherwise. In fact googling for ( "strongly typed" OpenSCAD ) has a
first result of Doug Moen explicitly saying "OpenSCAD isn't designed to be
a strongly typed language". I don't consider myself an expert on
programming linguistics by any means, but since Doug has previously put a
lot of thought and effort into the (unfortunately now defunct/abandoned I
guess) "OpenSCAD2" specification, I would defer to his knowledge on the
subject. Doug has replied in this thread already, though not specifically
addressing whether or not (or to what degree) OpenSCAD may be considered
strongly typed, but I would be all ears if he cares to elaborate any.
Generally when I combine
types in an operation OpenSCAD returns undef. This is why I suggested
OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
issuing an error.
OpenSCAD is perfectly capable of throwing errors and does so in many cases,
so I don't agree that returning undef is "equivalent" to issuing an error.
In the underlying implementation, undef is its own type, as is boolean,
and we don't use tri-state booleans. The very nature of returning undef
sometimes and bool/number/vector etc at other times makes this not
particularly strong in my opinion. The strongest response would be to stop
on error when two incompatible operands are used, but changing this
fundamental behavior would likely break a whole lot of things.
Comparison operators are currently implemented such that they
always return a boolean. Returning undef would mean redefining all of
these operators to return the variant (sum type) of "Value", making them
weaker in that sense.
In cases where the result of comparison is not being used directly in the
context of a condition, say it's assigned to a variable or a vector
element, then returning undef only serves to further this muddling of types.
On Sun, Jul 26, 2020 at 5:15 AM Parkinbot <rudolf@digitaldocument.de> wrote:
> I would opt for a clear and *consistent* implementation, which is neither
> the new nor the old implementation.
>
OK, but saying this alone is not helpful without explicitly defining what
*consistent* means to you.
So, would you be satisfied with what I proposed here (yes/no)? :
On Sun, Jul 26, 2020 at 1:44 AM Hans L <thehans@gmail.com> wrote:
> ... the change could have just as easily gone the other way: to make <=
> and >= always return false when comparing between numbers and bools.
>
> If we can agree that's what our users want/expect in general, then
> personally I would be fine with changing it around.
>
On Sun, Jul 26, 2020 at 7:51 AM adrianv <avm4@cornell.edu> wrote:
> Note that the change under discussion may have been made a while ago, but
> I,
> at least, don't run the nightly builds because I'm doing library
> development
> and the library is meant to run under the stable release.
>
I can certainly understand the desire to support stable releases for
libraries (i.e. not relying on experimental features). But as a library
developer it seems to me you also have a vested interest in future
development of OpenSCAD.
So while you may prefer to develop for (and on) the stable release, I would
strongly encourage you and other library authors to, at least periodically,
test against nightlies. This would really help identify any possible
breaking changes early on, before they become more permanently ingrained in
the *next* stable release.
OpenSCAD has a fairly comprehensive test suite which helps identify many
such regressions, but it's thoroughness is dependent on issues being raised
on Github with reproducible test cases etc.
Also, as a library author, I would assume you are more likely to make use
of esoteric / niche features of the language which more casual users may
not be aware of, which makes them even more valuable as test cases.
> In what way(s) is OpenSCAD not strongly typed?
I don't care to dive deeply into semantic arguments here, since it seems
that strong typing means different things, with various qualifications and
exceptions, depending on who/what language you ask.
What I specifically said was "... I don't believe anyone has ever claimed
[OpenSCAD] to be [strongly typed]" (with the exception of you, now). This
means, to the best of my knowledge, none of the documentation nor project
authors have made this claim.
If you want to argue against that, then the burden of proof is on you to
show otherwise. In fact googling for ( "strongly typed" OpenSCAD ) has a
first result of Doug Moen explicitly saying "OpenSCAD isn't designed to be
a strongly typed language". I don't consider myself an expert on
programming linguistics by any means, but since Doug has previously put a
lot of thought and effort into the (unfortunately now defunct/abandoned I
guess) "OpenSCAD2" specification, I would defer to his knowledge on the
subject. Doug has replied in this thread already, though not specifically
addressing whether or not (or to what degree) OpenSCAD may be considered
strongly typed, but I would be all ears if he cares to elaborate any.
> Generally when I combine
> types in an operation OpenSCAD returns undef. This is why I suggested
> OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
> issuing an error.
>
OpenSCAD is perfectly capable of throwing errors and does so in many cases,
so I don't agree that returning undef is "equivalent" to issuing an error.
In the underlying implementation, undef *is* its own type, as is boolean,
and we don't use tri-state booleans. The very nature of returning undef
sometimes and bool/number/vector etc at other times makes this not
particularly strong in my opinion. The strongest response would be to stop
on error when two incompatible operands are used, but changing this
fundamental behavior would likely break a whole lot of things.
Comparison operators are currently implemented such that they
always return a boolean. Returning undef would mean redefining all of
these operators to return the variant (sum type) of "Value", making them
weaker in that sense.
In cases where the result of comparison is not being used directly in the
context of a condition, say it's assigned to a variable or a vector
element, then returning undef only serves to further this muddling of types.
JW
Jan Wieck
Sun, Jul 26, 2020 10:07 PM
Treating booleans as numbers where zero means False and everything else
means True has served us well for decades in many programming laguages.
I understand the desire to treat booleans more special, but that doesn't
mean that I would support their behavior to become completely randomized or
non sensical. If you allow an expression of (one_bool EQ another_bool) and
the result isn't True if both are True or both are False, then you create a
huge footgun for no apparent reason whatsoever. Better make that throw an
error at compile time.
There is no real sense in having GT, LE and so on operators for booleans.
That should always throw an error early on. But equality and inequality
operators even exist in very well defined and designed language standards.
Being the oddball in this regard isn't going to serve us well.
Best Regards, Jan
On Sun, Jul 26, 2020 at 4:01 PM Hans L thehans@gmail.com wrote:
I would opt for a clear and consistent implementation, which is neither
the new nor the old implementation.
OK, but saying this alone is not helpful without explicitly defining what
consistent means to you.
So, would you be satisfied with what I proposed here (yes/no)? :
On Sun, Jul 26, 2020 at 1:44 AM Hans L thehans@gmail.com wrote:
... the change could have just as easily gone the other way: to make <=
and >= always return false when comparing between numbers and bools.
If we can agree that's what our users want/expect in general, then
personally I would be fine with changing it around.
Note that the change under discussion may have been made a while ago, but
I,
at least, don't run the nightly builds because I'm doing library
development
and the library is meant to run under the stable release.
I can certainly understand the desire to support stable releases for
libraries (i.e. not relying on experimental features). But as a library
developer it seems to me you also have a vested interest in future
development of OpenSCAD.
So while you may prefer to develop for (and on) the stable release, I
would strongly encourage you and other library authors to, at least
periodically, test against nightlies. This would really help identify any
possible breaking changes early on, before they become more permanently
ingrained in the next stable release.
OpenSCAD has a fairly comprehensive test suite which helps identify many
such regressions, but it's thoroughness is dependent on issues being raised
on Github with reproducible test cases etc.
Also, as a library author, I would assume you are more likely to make use
of esoteric / niche features of the language which more casual users may
not be aware of, which makes them even more valuable as test cases.
In what way(s) is OpenSCAD not strongly typed?
I don't care to dive deeply into semantic arguments here, since it seems
that strong typing means different things, with various qualifications and
exceptions, depending on who/what language you ask.
What I specifically said was "... I don't believe anyone has ever claimed
[OpenSCAD] to be [strongly typed]" (with the exception of you, now). This
means, to the best of my knowledge, none of the documentation nor project
authors have made this claim.
If you want to argue against that, then the burden of proof is on you to
show otherwise. In fact googling for ( "strongly typed" OpenSCAD ) has a
first result of Doug Moen explicitly saying "OpenSCAD isn't designed to be
a strongly typed language". I don't consider myself an expert on
programming linguistics by any means, but since Doug has previously put a
lot of thought and effort into the (unfortunately now defunct/abandoned I
guess) "OpenSCAD2" specification, I would defer to his knowledge on the
subject. Doug has replied in this thread already, though not specifically
addressing whether or not (or to what degree) OpenSCAD may be considered
strongly typed, but I would be all ears if he cares to elaborate any.
Generally when I combine
types in an operation OpenSCAD returns undef. This is why I suggested
OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
issuing an error.
OpenSCAD is perfectly capable of throwing errors and does so in many
cases, so I don't agree that returning undef is "equivalent" to issuing an
error.
In the underlying implementation, undef is its own type, as is boolean,
and we don't use tri-state booleans. The very nature of returning undef
sometimes and bool/number/vector etc at other times makes this not
particularly strong in my opinion. The strongest response would be to stop
on error when two incompatible operands are used, but changing this
fundamental behavior would likely break a whole lot of things.
Comparison operators are currently implemented such that they
always return a boolean. Returning undef would mean redefining all of
these operators to return the variant (sum type) of "Value", making them
weaker in that sense.
In cases where the result of comparison is not being used directly in the
context of a condition, say it's assigned to a variable or a vector
element, then returning undef only serves to further this muddling of types.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
Jan Wieck
Principal Database Engineer
Treating booleans as numbers where zero means False and everything else
means True has served us well for decades in many programming laguages.
I understand the desire to treat booleans more special, but that doesn't
mean that I would support their behavior to become completely randomized or
non sensical. If you allow an expression of (one_bool EQ another_bool) and
the result isn't True if both are True or both are False, then you create a
huge footgun for no apparent reason whatsoever. Better make that throw an
error at compile time.
There is no real sense in having GT, LE and so on operators for booleans.
That should always throw an error early on. But equality and inequality
operators even exist in very well defined and designed language standards.
Being the oddball in this regard isn't going to serve us well.
Best Regards, Jan
On Sun, Jul 26, 2020 at 4:01 PM Hans L <thehans@gmail.com> wrote:
> On Sun, Jul 26, 2020 at 5:15 AM Parkinbot <rudolf@digitaldocument.de>
> wrote:
>
>> I would opt for a clear and *consistent* implementation, which is neither
>> the new nor the old implementation.
>>
>
> OK, but saying this alone is not helpful without explicitly defining what
> *consistent* means to you.
> So, would you be satisfied with what I proposed here (yes/no)? :
>
> On Sun, Jul 26, 2020 at 1:44 AM Hans L <thehans@gmail.com> wrote:
>
>> ... the change could have just as easily gone the other way: to make <=
>> and >= always return false when comparing between numbers and bools.
>>
>> If we can agree that's what our users want/expect in general, then
>> personally I would be fine with changing it around.
>>
>
>
> On Sun, Jul 26, 2020 at 7:51 AM adrianv <avm4@cornell.edu> wrote:
>
>> Note that the change under discussion may have been made a while ago, but
>> I,
>> at least, don't run the nightly builds because I'm doing library
>> development
>> and the library is meant to run under the stable release.
>>
>
> I can certainly understand the desire to support stable releases for
> libraries (i.e. not relying on experimental features). But as a library
> developer it seems to me you also have a vested interest in future
> development of OpenSCAD.
> So while you may prefer to develop for (and on) the stable release, I
> would strongly encourage you and other library authors to, at least
> periodically, test against nightlies. This would really help identify any
> possible breaking changes early on, before they become more permanently
> ingrained in the *next* stable release.
> OpenSCAD has a fairly comprehensive test suite which helps identify many
> such regressions, but it's thoroughness is dependent on issues being raised
> on Github with reproducible test cases etc.
> Also, as a library author, I would assume you are more likely to make use
> of esoteric / niche features of the language which more casual users may
> not be aware of, which makes them even more valuable as test cases.
>
>
>> In what way(s) is OpenSCAD not strongly typed?
>
>
> I don't care to dive deeply into semantic arguments here, since it seems
> that strong typing means different things, with various qualifications and
> exceptions, depending on who/what language you ask.
> What I specifically said was "... I don't believe anyone has ever claimed
> [OpenSCAD] to be [strongly typed]" (with the exception of you, now). This
> means, to the best of my knowledge, none of the documentation nor project
> authors have made this claim.
> If you want to argue against that, then the burden of proof is on you to
> show otherwise. In fact googling for ( "strongly typed" OpenSCAD ) has a
> first result of Doug Moen explicitly saying "OpenSCAD isn't designed to be
> a strongly typed language". I don't consider myself an expert on
> programming linguistics by any means, but since Doug has previously put a
> lot of thought and effort into the (unfortunately now defunct/abandoned I
> guess) "OpenSCAD2" specification, I would defer to his knowledge on the
> subject. Doug has replied in this thread already, though not specifically
> addressing whether or not (or to what degree) OpenSCAD may be considered
> strongly typed, but I would be all ears if he cares to elaborate any.
>
>
>> Generally when I combine
>> types in an operation OpenSCAD returns undef. This is why I suggested
>> OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
>> issuing an error.
>>
>
> OpenSCAD is perfectly capable of throwing errors and does so in many
> cases, so I don't agree that returning undef is "equivalent" to issuing an
> error.
> In the underlying implementation, undef *is* its own type, as is boolean,
> and we don't use tri-state booleans. The very nature of returning undef
> sometimes and bool/number/vector etc at other times makes this not
> particularly strong in my opinion. The strongest response would be to stop
> on error when two incompatible operands are used, but changing this
> fundamental behavior would likely break a whole lot of things.
>
> Comparison operators are currently implemented such that they
> always return a boolean. Returning undef would mean redefining all of
> these operators to return the variant (sum type) of "Value", making them
> weaker in that sense.
> In cases where the result of comparison is not being used directly in the
> context of a condition, say it's assigned to a variable or a vector
> element, then returning undef only serves to further this muddling of types.
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
--
Jan Wieck
Principal Database Engineer
DM
Doug Moen
Sun, Jul 26, 2020 10:50 PM
Since my name was mentioned, I better reply.
I understand "strongly typed" to mean that each function has a well defined contract, in terms of the types of arguments that it accepts, and that this contract is enforced either at compile time or run time. An error is reported if arguments have the wrong type. The goal is to stop the program from running and report a problem if function arguments appear to be garbage. The alternative being to keep running and produce garbage in the end, and you have no idea where the program went wrong.
The OpenSCAD2 project evolved into a brand new language called Curv, which is strongly typed, with dynamic types. For example, the boolean operators only accept the values true and false as arguments. If you pass non-boolean arguments, the program aborts with an error and a detailed stack trace. This is the way I wish OpenSCAD worked, because it makes programs easier to debug, so I created my own 3D modelling language that works this way.
For the most part, OpenSCAD functions silently return undef when passed bad arguments. For example, 1+true is undef. I don't consider this to be "strongly typed" behaviour. However, changing OpenSCAD to be strongly typed would break a lot of existing code, which would be undesirable.
Doug Moen.
On Sun, Jul 26, 2020, at 3:59 PM, Hans L wrote:
I would opt for a clear and consistent implementation, which is neither
the new nor the old implementation.
OK, but saying this alone is not helpful without explicitly defining what consistent means to you.
So, would you be satisfied with what I proposed here (yes/no)? :
On Sun, Jul 26, 2020 at 1:44 AM Hans L thehans@gmail.com wrote:
... the change could have just as easily gone the other way: to make <= and >= always return false when comparing between numbers and bools.
If we can agree that's what our users want/expect in general, then personally I would be fine with changing it around.
Note that the change under discussion may have been made a while ago, but I,
at least, don't run the nightly builds because I'm doing library development
and the library is meant to run under the stable release.
I can certainly understand the desire to support stable releases for libraries (i.e. not relying on experimental features). But as a library developer it seems to me you also have a vested interest in future development of OpenSCAD.
So while you may prefer to develop for (and on) the stable release, I would strongly encourage you and other library authors to, at least periodically, test against nightlies. This would really help identify any possible breaking changes early on, before they become more permanently ingrained in the next stable release.
OpenSCAD has a fairly comprehensive test suite which helps identify many such regressions, but it's thoroughness is dependent on issues being raised on Github with reproducible test cases etc.
Also, as a library author, I would assume you are more likely to make use of esoteric / niche features of the language which more casual users may not be aware of, which makes them even more valuable as test cases.
In what way(s) is OpenSCAD not strongly typed?
I don't care to dive deeply into semantic arguments here, since it seems that strong typing means different things, with various qualifications and exceptions, depending on who/what language you ask.
What I specifically said was "... I don't believe anyone has ever claimed [OpenSCAD] to be [strongly typed]" (with the exception of you, now). This means, to the best of my knowledge, none of the documentation nor project authors have made this claim.
If you want to argue against that, then the burden of proof is on you to show otherwise. In fact googling for ( "strongly typed" OpenSCAD ) has a first result of Doug Moen explicitly saying "OpenSCAD isn't designed to be a strongly typed language". I don't consider myself an expert on programming linguistics by any means, but since Doug has previously put a lot of thought and effort into the (unfortunately now defunct/abandoned I guess) "OpenSCAD2" specification, I would defer to his knowledge on the subject. Doug has replied in this thread already, though not specifically addressing whether or not (or to what degree) OpenSCAD may be considered strongly typed, but I would be all ears if he cares to elaborate any.
Generally when I combine
types in an operation OpenSCAD returns undef. This is why I suggested
OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
issuing an error.
OpenSCAD is perfectly capable of throwing errors and does so in many cases, so I don't agree that returning undef is "equivalent" to issuing an error.
In the underlying implementation, undef is its own type, as is boolean, and we don't use tri-state booleans. The very nature of returning undef sometimes and bool/number/vector etc at other times makes this not particularly strong in my opinion. The strongest response would be to stop on error when two incompatible operands are used, but changing this fundamental behavior would likely break a whole lot of things.
Comparison operators are currently implemented such that they always return a boolean. Returning undef would mean redefining all of these operators to return the variant (sum type) of "Value", making them weaker in that sense.
In cases where the result of comparison is not being used directly in the context of a condition, say it's assigned to a variable or a vector element, then returning undef only serves to further this muddling of types.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Since my name was mentioned, I better reply.
I understand "strongly typed" to mean that each function has a well defined contract, in terms of the types of arguments that it accepts, and that this contract is enforced either at compile time or run time. An error is reported if arguments have the wrong type. The goal is to stop the program from running and report a problem if function arguments appear to be garbage. The alternative being to keep running and produce garbage in the end, and you have no idea where the program went wrong.
The OpenSCAD2 project evolved into a brand new language called Curv, which is strongly typed, with dynamic types. For example, the boolean operators only accept the values true and false as arguments. If you pass non-boolean arguments, the program aborts with an error and a detailed stack trace. This is the way I wish OpenSCAD worked, because it makes programs easier to debug, so I created my own 3D modelling language that works this way.
For the most part, OpenSCAD functions silently return undef when passed bad arguments. For example, 1+true is undef. I don't consider this to be "strongly typed" behaviour. However, changing OpenSCAD to be strongly typed would break a lot of existing code, which would be undesirable.
Doug Moen.
On Sun, Jul 26, 2020, at 3:59 PM, Hans L wrote:
> On Sun, Jul 26, 2020 at 5:15 AM Parkinbot <rudolf@digitaldocument.de> wrote:
>> I would opt for a clear and *consistent* implementation, which is neither
>> the new nor the old implementation.
>
> OK, but saying this alone is not helpful without explicitly defining what *consistent* means to you.
>
> So, would you be satisfied with what I proposed here (yes/no)? :
>
> On Sun, Jul 26, 2020 at 1:44 AM Hans L <thehans@gmail.com> wrote:
>> ... the change could have just as easily gone the other way: to make <= and >= always return false when comparing between numbers and bools.
>>
>> If we can agree that's what our users want/expect in general, then personally I would be fine with changing it around.
>
>
> On Sun, Jul 26, 2020 at 7:51 AM adrianv <avm4@cornell.edu> wrote:
>> Note that the change under discussion may have been made a while ago, but I,
>> at least, don't run the nightly builds because I'm doing library development
>> and the library is meant to run under the stable release.
>
> I can certainly understand the desire to support stable releases for libraries (i.e. not relying on experimental features). But as a library developer it seems to me you also have a vested interest in future development of OpenSCAD.
> So while you may prefer to develop for (and on) the stable release, I would strongly encourage you and other library authors to, at least periodically, test against nightlies. This would really help identify any possible breaking changes early on, before they become more permanently ingrained in the *next* stable release.
> OpenSCAD has a fairly comprehensive test suite which helps identify many such regressions, but it's thoroughness is dependent on issues being raised on Github with reproducible test cases etc.
> Also, as a library author, I would assume you are more likely to make use of esoteric / niche features of the language which more casual users may not be aware of, which makes them even more valuable as test cases.
>
>> In what way(s) is OpenSCAD not strongly typed?
>
> I don't care to dive deeply into semantic arguments here, since it seems that strong typing means different things, with various qualifications and exceptions, depending on who/what language you ask.
>
> What I specifically said was "... I don't believe anyone has ever claimed [OpenSCAD] to be [strongly typed]" (with the exception of you, now). This means, to the best of my knowledge, none of the documentation nor project authors have made this claim.
> If you want to argue against that, then the burden of proof is on you to show otherwise. In fact googling for ( "strongly typed" OpenSCAD ) has a first result of Doug Moen explicitly saying "OpenSCAD isn't designed to be a strongly typed language". I don't consider myself an expert on programming linguistics by any means, but since Doug has previously put a lot of thought and effort into the (unfortunately now defunct/abandoned I guess) "OpenSCAD2" specification, I would defer to his knowledge on the subject. Doug has replied in this thread already, though not specifically addressing whether or not (or to what degree) OpenSCAD may be considered strongly typed, but I would be all ears if he cares to elaborate any.
>
>
>
>> Generally when I combine
>> types in an operation OpenSCAD returns undef. This is why I suggested
>> OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
>> issuing an error.
>
> OpenSCAD is perfectly capable of throwing errors and does so in many cases, so I don't agree that returning undef is "equivalent" to issuing an error.
> In the underlying implementation, undef *is* its own type, as is boolean, and we don't use tri-state booleans. The very nature of returning undef sometimes and bool/number/vector etc at other times makes this not particularly strong in my opinion. The strongest response would be to stop on error when two incompatible operands are used, but changing this fundamental behavior would likely break a whole lot of things.
>
> Comparison operators are currently implemented such that they always return a boolean. Returning undef would mean redefining all of these operators to return the variant (sum type) of "Value", making them weaker in that sense.
> In cases where the result of comparison is not being used directly in the context of a condition, say it's assigned to a variable or a vector element, then returning undef only serves to further this muddling of types.
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Sun, Jul 26, 2020 11:08 PM
On Sun, Jul 26, 2020 at 5:15 AM Parkinbot <
I would opt for a clear and consistent implementation, which is neither
the new nor the old implementation.
OK, but saying this alone is not helpful without explicitly defining what
consistent means to you.
So, would you be satisfied with what I proposed here (yes/no)? :
On Sun, Jul 26, 2020 at 1:44 AM Hans L <
... the change could have just as easily gone the other way: to make <=
and >= always return false when comparing between numbers and bools.
If we can agree that's what our users want/expect in general, then
personally I would be fine with changing it around.
I'm not sure if you were asking me or Parkinbot (who you quoted).
Ultimately I don't particularly care what relative comparison operators do
when given booleans. It's a pointless combination that should never be
used. If you return false for all cases that's fine with me. I think
returning undef for all relative compares between different types is better
and more consistent across the language. But ultimately, it doesn't matter.
What does matter is the behavior of equality testing, where I insist that
(x==true) is true only when x is the actual boolean true and not for any
other value. And similarly (x==false) is true if and only if x is the
actual boolean value false.
On Sun, Jul 26, 2020 at 7:51 AM adrianv <
Note that the change under discussion may have been made a while ago, but
I,
at least, don't run the nightly builds because I'm doing library
development
and the library is meant to run under the stable release.
I can certainly understand the desire to support stable releases for
libraries (i.e. not relying on experimental features). But as a library
developer it seems to me you also have a vested interest in future
development of OpenSCAD.
So while you may prefer to develop for (and on) the stable release, I
would
strongly encourage you and other library authors to, at least
periodically,
test against nightlies. This would really help identify any possible
breaking changes early on, before they become more permanently ingrained
in
the next stable release.
OpenSCAD has a fairly comprehensive test suite which helps identify many
such regressions, but it's thoroughness is dependent on issues being
raised
on Github with reproducible test cases etc.
Also, as a library author, I would assume you are more likely to make use
of esoteric / niche features of the language which more casual users may
not be aware of, which makes them even more valuable as test cases.
Indeed, this situation has made clear the need for testing against the
development versions. We're building up regression tests for the functions
in the library, which should make it possible to catch problems created by
changes in OpenSCAD---if we write our regression tests well. We don't have
any mechanism to test geometry.
Generally when I combine
types in an operation OpenSCAD returns undef. This is why I suggested
OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
issuing an error.
OpenSCAD is perfectly capable of throwing errors and does so in many
cases,
so I don't agree that returning undef is "equivalent" to issuing an error.
I would say that OpenSCAD may occasionally issue errors, but for most error
conditions it instead confounds the programmer by returning undef instead,
so that the error can propagate along ten functions more before finally
producing an obvious problem. I've spent hours debugging code just
tracking down where errors occur because of this behavior, where an
immediate error would have directed me immediately to the problem. This
has lead me to the philosophy that undef should never be intentionally
returned by any function. It's kind of too bad since the notion of undef is
useful, e.g. for returning the intersection point of two parallel lines.
But it seems more important to do as much as possible to distinguish
meaningful returns from programming errors.
Comparison operators are currently implemented such that they
always return a boolean. Returning undef would mean redefining all of
these operators to return the variant (sum type) of "Value", making them
weaker in that sense.
In cases where the result of comparison is not being used directly in the
context of a condition, say it's assigned to a variable or a vector
element, then returning undef only serves to further this muddling of
types.
The change that prompted this discussion had to do with establishing
consistency. I maintain that consistency is maximized if comparisons
between different types return undef, which is kind of the "no type" type.
I don't think it particularly muddles types---certainly not any more than
the standard where x+y can be undef. It sounds like this is an annoying
change in the code. I don't really see that this consistency has much
practical value and would not want programmers to spend their time on this.
Returning false for all inter-type comparisons is reasonably consistent as
well.
--
Sent from: http://forum.openscad.org/
thehans wrote
> On Sun, Jul 26, 2020 at 5:15 AM Parkinbot <
> rudolf@
> > wrote:
>
>> I would opt for a clear and *consistent* implementation, which is neither
>> the new nor the old implementation.
>>
>
> OK, but saying this alone is not helpful without explicitly defining what
> *consistent* means to you.
> So, would you be satisfied with what I proposed here (yes/no)? :
>
> On Sun, Jul 26, 2020 at 1:44 AM Hans L <
> thehans@
> > wrote:
>
>> ... the change could have just as easily gone the other way: to make <=
>> and >= always return false when comparing between numbers and bools.
>>
>> If we can agree that's what our users want/expect in general, then
>> personally I would be fine with changing it around.
>>
I'm not sure if you were asking me or Parkinbot (who you quoted).
Ultimately I don't particularly care what relative comparison operators do
when given booleans. It's a pointless combination that should never be
used. If you return false for all cases that's fine with me. I think
returning undef for all relative compares between different types is better
and more consistent across the language. But ultimately, it doesn't matter.
What does matter is the behavior of equality testing, where I insist that
(x==true) is true only when x is the actual boolean true and not for any
other value. And similarly (x==false) is true if and only if x is the
actual boolean value false.
> On Sun, Jul 26, 2020 at 7:51 AM adrianv <
> avm4@
> > wrote:
>
>> Note that the change under discussion may have been made a while ago, but
>> I,
>> at least, don't run the nightly builds because I'm doing library
>> development
>> and the library is meant to run under the stable release.
>>
>
> I can certainly understand the desire to support stable releases for
> libraries (i.e. not relying on experimental features). But as a library
> developer it seems to me you also have a vested interest in future
> development of OpenSCAD.
> So while you may prefer to develop for (and on) the stable release, I
> would
> strongly encourage you and other library authors to, at least
> periodically,
> test against nightlies. This would really help identify any possible
> breaking changes early on, before they become more permanently ingrained
> in
> the *next* stable release.
> OpenSCAD has a fairly comprehensive test suite which helps identify many
> such regressions, but it's thoroughness is dependent on issues being
> raised
> on Github with reproducible test cases etc.
> Also, as a library author, I would assume you are more likely to make use
> of esoteric / niche features of the language which more casual users may
> not be aware of, which makes them even more valuable as test cases.
Indeed, this situation has made clear the need for testing against the
development versions. We're building up regression tests for the functions
in the library, which should make it possible to catch problems created by
changes in OpenSCAD---if we write our regression tests well. We don't have
any mechanism to test geometry.
>> Generally when I combine
>> types in an operation OpenSCAD returns undef. This is why I suggested
>> OpenSCAD was strongly typed, since this is the OpenSCAD equivalent of
>> issuing an error.
>>
>
> OpenSCAD is perfectly capable of throwing errors and does so in many
> cases,
> so I don't agree that returning undef is "equivalent" to issuing an error.
I would say that OpenSCAD may occasionally issue errors, but for most error
conditions it instead confounds the programmer by returning undef instead,
so that the error can propagate along ten functions more before finally
producing an obvious problem. I've spent hours debugging code just
tracking down where errors occur because of this behavior, where an
immediate error would have directed me immediately to the problem. This
has lead me to the philosophy that undef should never be intentionally
returned by any function. It's kind of too bad since the notion of undef is
useful, e.g. for returning the intersection point of two parallel lines.
But it seems more important to do as much as possible to distinguish
meaningful returns from programming errors.
> Comparison operators are currently implemented such that they
> always return a boolean. Returning undef would mean redefining all of
> these operators to return the variant (sum type) of "Value", making them
> weaker in that sense.
> In cases where the result of comparison is not being used directly in the
> context of a condition, say it's assigned to a variable or a vector
> element, then returning undef only serves to further this muddling of
> types.
The change that prompted this discussion had to do with establishing
consistency. I maintain that consistency is maximized if comparisons
between different types return undef, which is kind of the "no type" type.
I don't think it particularly muddles types---certainly not any more than
the standard where x+y can be undef. It sounds like this is an annoying
change in the code. I don't really see that this consistency has much
practical value and would not want programmers to spend their time on this.
Returning false for all inter-type comparisons is reasonably consistent as
well.
--
Sent from: http://forum.openscad.org/
P
Parkinbot
Mon, Jul 27, 2020 7:20 AM
Parkinbot, I don't understand what you mean. Right now undef is treated
as
false in a boolean context, and nobody is suggesting that this change. So
someone who does comparisons like "if (true<3)"
will get the
same result if such comparisons return false as if they return undef.
I think, I was very clear in what I wrote and what I am opting for:
1: Apples are no pears -> if operands have different types, "false" results
2: tertium non datur -> a boolean operation returns a boolean value, i.e.
"true" or "false" but nothing else.
identifying "undef" and "false" and allowing "undef" as result of an
apples/pears comparison would introduce a third value and thus allow for
implicit type checks, not more and not less. But with the new is_XXX()
functions we have means to do explicit type checks, why introduce/support
implicit ones?
if(undef != (op1 == op2)) // this would be a generic type check, if undef
is introduced
--
Sent from: http://forum.openscad.org/
adrianv wrote
> Parkinbot, I don't understand what you mean. Right now undef is treated
> as
> false in a boolean context, and nobody is suggesting that this change. So
> someone who does comparisons like &quot;if (true&lt;3)&quot;
> will get the
> same result if such comparisons return false as if they return undef.
I think, I was very clear in what I wrote and what I am opting for:
1: Apples are no pears -> if operands have different types, "false" results
2: tertium non datur -> a boolean operation returns a boolean value, i.e.
"true" or "false" but nothing else.
identifying "undef" and "false" and allowing "undef" as result of an
apples/pears comparison would introduce a third value and thus allow for
implicit type checks, not more and not less. But with the new is_XXX()
functions we have means to do explicit type checks, why introduce/support
implicit ones?
if(undef != (op1 == op2)) // this would be a generic type check, if undef
is introduced
--
Sent from: http://forum.openscad.org/
P
Parkinbot
Mon, Jul 27, 2020 7:38 AM
However, with an implementation that follows rules 1 and 2 of my previous
post, you still can do such a generic type check by writing:
function is_same_type(op1, o2) = (op1==op2)!=(op1!=op2);
--
Sent from: http://forum.openscad.org/
However, with an implementation that follows rules 1 and 2 of my previous
post, you still can do such a generic type check by writing:
function is_same_type(op1, o2) = (op1==op2)!=(op1!=op2);
--
Sent from: http://forum.openscad.org/
P
Parkinbot
Mon, Jul 27, 2020 10:15 AM
Another issue of allowing "undef" as result of a logical operation:
What is the value of
(false == undef)?
It obviously can't be "true", otherwise you could not distinguish between
these two values, and if it is "false" you get the following contradiction.
The expression
(1 == "1") == false
would evaluate to
undef == false
and this evalulates to false (or undef?). However "true" is expected.
This is all a can of worms.
--
Sent from: http://forum.openscad.org/
Another issue of allowing "undef" as result of a logical operation:
What is the value of
(false == undef)?
It obviously can't be "true", otherwise you could not distinguish between
these two values, and if it is "false" you get the following contradiction.
The expression
(1 == "1") == false
would evaluate to
undef == false
and this evalulates to false (or undef?). However "true" is expected.
This is all a can of worms.
--
Sent from: http://forum.openscad.org/
M
MathLover
Mon, Jul 27, 2020 11:41 AM
If I may add something to the discussion, I would really like booleans to be
booleans. Also, I LIKE error messages if I am doing something that just does
not make sense. As someone once said, the language should talk to you
(meaning the language gives useful error messages rather than assume
nonsense and do unexpected things).
So if I would compare a boolean with greater or lesser operators, I expect
an error message. True is not more or less than false.
Microsoft languages like Visual Basic used to have a "feature" called "Null
progagation". This meant that if you used a Null value (which would also be
undef) anywhere in an expression, the outcome of that expression would also
be Null. This off course broke all boolean operations, because Null would be
treated as false.
Yes, ("Some value" != null) would thus be treated as false and even (! null)
would be treated the same as null, as false.
This made programming visual basic and other languages that had this
"feature" a minefield when dealing with booleans. So please don't go that
route. I have been there, and I really do not want to go back.
--
Sent from: http://forum.openscad.org/
If I may add something to the discussion, I would really like booleans to be
booleans. Also, I LIKE error messages if I am doing something that just does
not make sense. As someone once said, the language should talk to you
(meaning the language gives useful error messages rather than assume
nonsense and do unexpected things).
So if I would compare a boolean with greater or lesser operators, I expect
an error message. True is not more or less than false.
Microsoft languages like Visual Basic used to have a "feature" called "Null
progagation". This meant that if you used a Null value (which would also be
undef) anywhere in an expression, the outcome of that expression would also
be Null. This off course broke all boolean operations, because Null would be
treated as false.
Yes, ("Some value" != null) would thus be treated as false and even (! null)
would be treated the same as null, as false.
This made programming visual basic and other languages that had this
"feature" a minefield when dealing with booleans. So please don't go that
route. I have been there, and I really do not want to go back.
--
Sent from: http://forum.openscad.org/
A
adrianv
Mon, Jul 27, 2020 12:44 PM
Another issue of allowing "undef" as result of a logical operation:
What is the value of
(false == undef)?
It obviously can't be "true", otherwise you could not distinguish between
these two values, and if it is "false" you get the following
contradiction.
The expression
(1 == "1") == false
would evaluate to
undef == false
and this evalulates to false (or undef?). However "true" is expected.
This is all a can of worms.
I am not sure what gave you the idea that equality or inequality testing
should result in undef under any circumstances. That would be very bad.
Equality testing is well-defined for any pair of objects, they are either
the same (in which case you return true) or they are different (in which
case you return false). Things with different types are clearly different.
Inequality tests are different. They are undefined (meaningless) when given
arguments of different type. Hence my proposal was simply that inequality
tests (<, >, <=, =>) result in undef when given mismatched types. I'd say
printing an error message would be even better.
--
Sent from: http://forum.openscad.org/
Parkinbot wrote
> Another issue of allowing "undef" as result of a logical operation:
>
> What is the value of
>
> (false == undef)?
>
> It obviously can't be "true", otherwise you could not distinguish between
> these two values, and if it is "false" you get the following
> contradiction.
> The expression
>
> (1 == "1") == false
>
> would evaluate to
>
> undef == false
>
> and this evalulates to false (or undef?). However "true" is expected.
> This is all a can of worms.
I am not sure what gave you the idea that equality or inequality testing
should result in undef under any circumstances. That would be very bad.
Equality testing is well-defined for any pair of objects, they are either
the same (in which case you return true) or they are different (in which
case you return false). Things with different types are clearly different.
Inequality tests are different. They are undefined (meaningless) when given
arguments of different type. Hence my proposal was simply that inequality
tests (<, >, <=, =>) result in undef when given mismatched types. I'd say
printing an error message would be even better.
--
Sent from: http://forum.openscad.org/
A
adrianv
Mon, Jul 27, 2020 12:50 PM
Microsoft languages like Visual Basic used to have a "feature" called
"Null
progagation". This meant that if you used a Null value (which would also
be
undef) anywhere in an expression, the outcome of that expression would
also
be Null. This off course broke all boolean operations, because Null would
be
treated as false.
Yes, ("Some value" != null) would thus be treated as false and even (!
null)
would be treated the same as null, as false.
This made programming visual basic and other languages that had this
"feature" a minefield when dealing with booleans. So please don't go that
route. I have been there, and I really do not want to go back.
OpenSCAD already has this feature with undef. And yes indeed, it makes
debugging a nightmare.
Right now any invalid operation---except for a boolean comparison---returns
undef. And any operation containing an undef---except for equality and
inequality testing---produces an undef as a result.
Index out of bounds? You get undef instead of an error. It would be a
thousand times better if all of these things produced immediate error
messages.
So yes, it would make sense for boolean operations on mismatched types to
produce an error message. The reason I didn't suggest that as a possibility
is consistency: all the other bogus operations in OpenSCAD produce undef
instead of an error.
--
Sent from: http://forum.openscad.org/
MathLover wrote
> Microsoft languages like Visual Basic used to have a "feature" called
> "Null
> progagation". This meant that if you used a Null value (which would also
> be
> undef) anywhere in an expression, the outcome of that expression would
> also
> be Null. This off course broke all boolean operations, because Null would
> be
> treated as false.
>
> Yes, ("Some value" != null) would thus be treated as false and even (!
> null)
> would be treated the same as null, as false.
>
> This made programming visual basic and other languages that had this
> "feature" a minefield when dealing with booleans. So please don't go that
> route. I have been there, and I really do not want to go back.
OpenSCAD already has this feature with undef. And yes indeed, it makes
debugging a nightmare.
Right now any invalid operation---except for a boolean comparison---returns
undef. And any operation containing an undef---except for equality and
inequality testing---produces an undef as a result.
Index out of bounds? You get undef instead of an error. It would be a
thousand times better if all of these things produced immediate error
messages.
So yes, it would make sense for boolean operations on mismatched types to
produce an error message. The reason I didn't suggest that as a possibility
is consistency: all the other bogus operations in OpenSCAD produce undef
instead of an error.
--
Sent from: http://forum.openscad.org/
RW
Ray West
Mon, Jul 27, 2020 1:23 PM
Perhaps the solution is to have two types of boolean - a true boolean,
and a false boolean. The true boolean only has two members, true and
false, a false boolean can have as many members as you wish, but you
define them, and their relationship to each other. A true boolean can
only have too operators '==' and '!=', a false boolean can have whatever
operators you want. , but you'll need to define your own hierarchy.
Basically, in this scenario, a false boolean is everything else, which
may be further divided into strings, floating point numbers, colours,
whatever.
Historically, There has been a lot of misunderstanding about the true
nature of boolean, by folk who should have known better. Just because
digital computers work the way they do, they bypassed they forgot about
what the ones and zeros truly represented, and manipulated them in the
ways that ones and zeros would be manipulated as normal
bits/bytes/whatever. I blame their parents.
I guess the real concern is that openscad programs are interpreted, not
compiled, so older programs would not work properly in a newer
interpreter. Perhaps an interpreter version number could be included in
the data. (sort of like Microsoft .net asks for versions, but that is
hopefully backwards compatible). Whatever is decided, not everybody will
be happy, but at least we're trying.
Best wishes,
Ray
On 27/07/2020 12:41, MathLover wrote:
If I may add something to the discussion, I would really like booleans to be
booleans. Also, I LIKE error messages if I am doing something that just does
not make sense. As someone once said, the language should talk to you
(meaning the language gives useful error messages rather than assume
nonsense and do unexpected things).
So if I would compare a boolean with greater or lesser operators, I expect
an error message. True is not more or less than false.
Microsoft languages like Visual Basic used to have a "feature" called "Null
progagation". This meant that if you used a Null value (which would also be
undef) anywhere in an expression, the outcome of that expression would also
be Null. This off course broke all boolean operations, because Null would be
treated as false.
Yes, ("Some value" != null) would thus be treated as false and even (! null)
would be treated the same as null, as false.
This made programming visual basic and other languages that had this
"feature" a minefield when dealing with booleans. So please don't go that
route. I have been there, and I really do not want to go back.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Perhaps the solution is to have two types of boolean - a true boolean,
and a false boolean. The true boolean only has two members, true and
false, a false boolean can have as many members as you wish, but you
define them, and their relationship to each other. A true boolean can
only have too operators '==' and '!=', a false boolean can have whatever
operators you want. , but you'll need to define your own hierarchy.
Basically, in this scenario, a false boolean is everything else, which
may be further divided into strings, floating point numbers, colours,
whatever.
Historically, There has been a lot of misunderstanding about the true
nature of boolean, by folk who should have known better. Just because
digital computers work the way they do, they bypassed they forgot about
what the ones and zeros truly represented, and manipulated them in the
ways that ones and zeros would be manipulated as normal
bits/bytes/whatever. I blame their parents.
I guess the real concern is that openscad programs are interpreted, not
compiled, so older programs would not work properly in a newer
interpreter. Perhaps an interpreter version number could be included in
the data. (sort of like Microsoft .net asks for versions, but that is
hopefully backwards compatible). Whatever is decided, not everybody will
be happy, but at least we're trying.
Best wishes,
Ray
On 27/07/2020 12:41, MathLover wrote:
> If I may add something to the discussion, I would really like booleans to be
> booleans. Also, I LIKE error messages if I am doing something that just does
> not make sense. As someone once said, the language should talk to you
> (meaning the language gives useful error messages rather than assume
> nonsense and do unexpected things).
>
> So if I would compare a boolean with greater or lesser operators, I expect
> an error message. True is not more or less than false.
>
> Microsoft languages like Visual Basic used to have a "feature" called "Null
> progagation". This meant that if you used a Null value (which would also be
> undef) anywhere in an expression, the outcome of that expression would also
> be Null. This off course broke all boolean operations, because Null would be
> treated as false.
>
> Yes, ("Some value" != null) would thus be treated as false and even (! null)
> would be treated the same as null, as false.
>
> This made programming visual basic and other languages that had this
> "feature" a minefield when dealing with booleans. So please don't go that
> route. I have been there, and I really do not want to go back.
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
RP
Ronaldo Persiano
Mon, Jul 27, 2020 2:36 PM
'undef' is a nightmare indeed. But it is not the only one: there are 'nan'
and 'inf' too which can turn into undef in the next operation. Should they
be eradicated? Certainly not, they have their place, at least 'undef' has.
To avoid nightmares, to have peace of mind, we need discipline. And
consistent emergence of these values in operations.
What should be the result of a polygon triangulation function if the
polygon has self-intersection? I would say that 'undef' (meaning "no
solution possible") is the best option. To generate an error or an empty
list (or some other signaling value) could be an alternative. The first
seems radical because it is so hard to find whether a polygon has
self-intersection as trying to triangulate it. The triangulation function
would be the check! The other options are falsely safe because a careless
coder may get in trouble later trying to access an element of an empty
list, for instance. There is no way to protect a careless code.That means
we need to always be careful with operations that might have "unusual"
results. That is a place for 'undef'.
Definitely an inequality expression involving pears and apples should
result in 'undef' (as we don't have a complete type order). That is
consistent with the result of trying to divide pears by a number or access
an array component of a single value (and that doesn't mean 'undef' is
another number value or another list) (*). But, yes, a test, either in an
if() or in a ternary expression, of 'undef' should be an error. If you
don't generate the error, the propagation will be uncontrollable. That is
the devil's place. 'undef' should not be taken as 'false'. Why not allow
the 'undef' value of an inequality be set to a variable? Or checked against
'undef' ? 'undef' is 'undef'.
(*) Dividing or multiplying a numerical vector by a scalar is a well
defined operation on linear algebra. There are good well defined
exceptions to the rule that 'pears' can't mix with 'apples'.
'undef' is a nightmare indeed. But it is not the only one: there are 'nan'
and 'inf' too which can turn into undef in the next operation. Should they
be eradicated? Certainly not, they have their place, at least 'undef' has.
To avoid nightmares, to have peace of mind, we need discipline. And
consistent emergence of these values in operations.
What should be the result of a polygon triangulation function if the
polygon has self-intersection? I would say that 'undef' (meaning "no
solution possible") is the best option. To generate an error or an empty
list (or some other signaling value) could be an alternative. The first
seems radical because it is so hard to find whether a polygon has
self-intersection as trying to triangulate it. The triangulation function
would be the check! The other options are falsely safe because a careless
coder may get in trouble later trying to access an element of an empty
list, for instance. There is no way to protect a careless code.That means
we need to always be careful with operations that might have "unusual"
results. That is a place for 'undef'.
Definitely an inequality expression involving pears and apples should
result in 'undef' (as we don't have a complete type order). That is
consistent with the result of trying to divide pears by a number or access
an array component of a single value (and that doesn't mean 'undef' is
another number value or another list) (*). But, yes, a test, either in an
if() or in a ternary expression, of 'undef' should be an error. If you
don't generate the error, the propagation will be uncontrollable. That is
the devil's place. 'undef' should not be taken as 'false'. Why not allow
the 'undef' value of an inequality be set to a variable? Or checked against
'undef' ? *'undef' is 'undef'*.
(*) Dividing or multiplying a numerical vector by a scalar is a well
defined operation on linear algebra. There are good well defined
exceptions to the rule that 'pears' can't mix with 'apples'.
NH
nop head
Mon, Jul 27, 2020 2:50 PM
If testing undef was an error it would break a lot of my code.
I often use if(list[n]) to mean if the list has a non zero nth element. If
the list doesn't have that element I get undef, which is false. I would
have to guard the test with a check of the length, which is less efficient
and more verbose.
On Mon, 27 Jul 2020 at 15:37, Ronaldo Persiano rcmpersiano@gmail.com
wrote:
'undef' is a nightmare indeed. But it is not the only one: there are 'nan'
and 'inf' too which can turn into undef in the next operation. Should they
be eradicated? Certainly not, they have their place, at least 'undef' has.
To avoid nightmares, to have peace of mind, we need discipline. And
consistent emergence of these values in operations.
What should be the result of a polygon triangulation function if the
polygon has self-intersection? I would say that 'undef' (meaning "no
solution possible") is the best option. To generate an error or an empty
list (or some other signaling value) could be an alternative. The first
seems radical because it is so hard to find whether a polygon has
self-intersection as trying to triangulate it. The triangulation function
would be the check! The other options are falsely safe because a careless
coder may get in trouble later trying to access an element of an empty
list, for instance. There is no way to protect a careless code.That means
we need to always be careful with operations that might have "unusual"
results. That is a place for 'undef'.
Definitely an inequality expression involving pears and apples should
result in 'undef' (as we don't have a complete type order). That is
consistent with the result of trying to divide pears by a number or access
an array component of a single value (and that doesn't mean 'undef' is
another number value or another list) (*). But, yes, a test, either in an
if() or in a ternary expression, of 'undef' should be an error. If you
don't generate the error, the propagation will be uncontrollable. That is
the devil's place. 'undef' should not be taken as 'false'. Why not allow
the 'undef' value of an inequality be set to a variable? Or checked against
'undef' ? 'undef' is 'undef'.
(*) Dividing or multiplying a numerical vector by a scalar is a well
defined operation on linear algebra. There are good well defined
exceptions to the rule that 'pears' can't mix with 'apples'.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
If testing undef was an error it would break a lot of my code.
I often use if(list[n]) to mean if the list has a non zero nth element. If
the list doesn't have that element I get undef, which is false. I would
have to guard the test with a check of the length, which is less efficient
and more verbose.
On Mon, 27 Jul 2020 at 15:37, Ronaldo Persiano <rcmpersiano@gmail.com>
wrote:
> 'undef' is a nightmare indeed. But it is not the only one: there are 'nan'
> and 'inf' too which can turn into undef in the next operation. Should they
> be eradicated? Certainly not, they have their place, at least 'undef' has.
> To avoid nightmares, to have peace of mind, we need discipline. And
> consistent emergence of these values in operations.
>
> What should be the result of a polygon triangulation function if the
> polygon has self-intersection? I would say that 'undef' (meaning "no
> solution possible") is the best option. To generate an error or an empty
> list (or some other signaling value) could be an alternative. The first
> seems radical because it is so hard to find whether a polygon has
> self-intersection as trying to triangulate it. The triangulation function
> would be the check! The other options are falsely safe because a careless
> coder may get in trouble later trying to access an element of an empty
> list, for instance. There is no way to protect a careless code.That means
> we need to always be careful with operations that might have "unusual"
> results. That is a place for 'undef'.
>
> Definitely an inequality expression involving pears and apples should
> result in 'undef' (as we don't have a complete type order). That is
> consistent with the result of trying to divide pears by a number or access
> an array component of a single value (and that doesn't mean 'undef' is
> another number value or another list) (*). But, yes, a test, either in an
> if() or in a ternary expression, of 'undef' should be an error. If you
> don't generate the error, the propagation will be uncontrollable. That is
> the devil's place. 'undef' should not be taken as 'false'. Why not allow
> the 'undef' value of an inequality be set to a variable? Or checked against
> 'undef' ? *'undef' is 'undef'*.
>
> (*) Dividing or multiplying a numerical vector by a scalar is a well
> defined operation on linear algebra. There are good well defined
> exceptions to the rule that 'pears' can't mix with 'apples'.
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Mon, Jul 27, 2020 3:04 PM
I agree that undef is a useful concept and would be great if OpenSCAD
operated with it differently.
What should happen is that any operation involving an undef is an immediate
fatal error, producing an error message---with the exception of the two
tests x==undef and x!=undef. If OpenSCAD worked like this, then I would
feel comfortable using undef to report a variety of results where
"undefined" is actually the right answer, such as the previously mentioned
intersection point of parallel lines, or the example below of
self-intersecting polygons.
Instead undef is actually the generic signal that the programmer made a
mistake. In this case, I feel it is essential to avoid ever intentionally
producing undef because then you can't distinguish between an error and a
case of valid return. Suppose your triangulation code has a bug and
sometimes returns undef as a result of this bug. You can't distinguish that
situation from the case that the triangulation identified a
self-intersection. If errors produced an error message and program halt
then a mistake in your code is easily distinguished from the undef return,
which happens in the self-intersecting polygon case. But since errors
produce undef, it is critical that in any code, one tries to handle all the
errors and never produce undef intentionally as an output. In this way we
know that if we see undef, there's a programming bug. Otherwise, we can't
distinguish the case of a programming bug from a valid "undefined" return
value.
Ronaldo wrote
'undef' is a nightmare indeed. But it is not the only one: there are 'nan'
and 'inf' too which can turn into undef in the next operation. Should they
be eradicated? Certainly not, they have their place, at least 'undef' has.
To avoid nightmares, to have peace of mind, we need discipline. And
consistent emergence of these values in operations.
What should be the result of a polygon triangulation function if the
polygon has self-intersection? I would say that 'undef' (meaning "no
solution possible") is the best option. To generate an error or an empty
list (or some other signaling value) could be an alternative. The first
seems radical because it is so hard to find whether a polygon has
self-intersection as trying to triangulate it. The triangulation function
would be the check! The other options are falsely safe because a careless
coder may get in trouble later trying to access an element of an empty
list, for instance. There is no way to protect a careless code.That means
we need to always be careful with operations that might have "unusual"
results. That is a place for 'undef'.
Definitely an inequality expression involving pears and apples should
result in 'undef' (as we don't have a complete type order). That is
consistent with the result of trying to divide pears by a number or access
an array component of a single value (and that doesn't mean 'undef' is
another number value or another list) (*). But, yes, a test, either in an
if() or in a ternary expression, of 'undef' should be an error. If you
don't generate the error, the propagation will be uncontrollable. That is
the devil's place. 'undef' should not be taken as 'false'. Why not allow
the 'undef' value of an inequality be set to a variable? Or checked
against
'undef' ? 'undef' is 'undef'.
I agree that undef is a useful concept and would be great if OpenSCAD
operated with it differently.
What should happen is that any operation involving an undef is an immediate
fatal error, producing an error message---with the exception of the two
tests x==undef and x!=undef. If OpenSCAD worked like this, then I would
feel comfortable using undef to report a variety of results where
"undefined" is actually the right answer, such as the previously mentioned
intersection point of parallel lines, or the example below of
self-intersecting polygons.
Instead undef is actually the generic signal that the programmer made a
mistake. In this case, I feel it is essential to avoid ever intentionally
producing undef because then you can't distinguish between an error and a
case of valid return. Suppose your triangulation code has a bug and
sometimes returns undef as a result of this bug. You can't distinguish that
situation from the case that the triangulation identified a
self-intersection. If errors produced an error message and program halt
then a mistake in your code is easily distinguished from the undef return,
which happens in the self-intersecting polygon case. But since errors
produce undef, it is critical that in any code, one tries to handle all the
errors and never produce undef intentionally as an output. In this way we
know that if we see undef, there's a programming bug. Otherwise, we can't
distinguish the case of a programming bug from a valid "undefined" return
value.
Ronaldo wrote
> 'undef' is a nightmare indeed. But it is not the only one: there are 'nan'
> and 'inf' too which can turn into undef in the next operation. Should they
> be eradicated? Certainly not, they have their place, at least 'undef' has.
> To avoid nightmares, to have peace of mind, we need discipline. And
> consistent emergence of these values in operations.
>
> What should be the result of a polygon triangulation function if the
> polygon has self-intersection? I would say that 'undef' (meaning "no
> solution possible") is the best option. To generate an error or an empty
> list (or some other signaling value) could be an alternative. The first
> seems radical because it is so hard to find whether a polygon has
> self-intersection as trying to triangulate it. The triangulation function
> would be the check! The other options are falsely safe because a careless
> coder may get in trouble later trying to access an element of an empty
> list, for instance. There is no way to protect a careless code.That means
> we need to always be careful with operations that might have "unusual"
> results. That is a place for 'undef'.
>
> Definitely an inequality expression involving pears and apples should
> result in 'undef' (as we don't have a complete type order). That is
> consistent with the result of trying to divide pears by a number or access
> an array component of a single value (and that doesn't mean 'undef' is
> another number value or another list) (*). But, yes, a test, either in an
> if() or in a ternary expression, of 'undef' should be an error. If you
> don't generate the error, the propagation will be uncontrollable. That is
> the devil's place. 'undef' should not be taken as 'false'. Why not allow
> the 'undef' value of an inequality be set to a variable? Or checked
> against
> 'undef' ? *'undef' is 'undef'*.
--
Sent from: http://forum.openscad.org/
A
adrianv
Mon, Jul 27, 2020 3:33 PM
The efficiency argument is weak in this case. I tested the run time for
len(foo) >=1001 ? foo[1000] : -1
compared to
foo[1000] ? foo[1000] : -1
where foo had length smaller than 1001.
I ran this 10 millions times. The run time for the length check was 1
microsecond. The undef test shaved this down to 0.7 microseconds.
I cannot conceive of a way that a different of 0.3 microseconds is important
in an OpenSCAD program.
The problem of your code being broken is the serious one. Personally if I
had a body of code written that way and the option to change OpenSCAD to
produce errors with undef I'd jump for errors in a heartbeat. The amount of
time it would take me to fix a megabyte of code would be less than the time
I've wasted already debugging undefs that propagate through my code. But
the backward compatibility problem is a definite problem. Perhaps a
setting could be introduced to optionally make undefs into fatal errors,
similar to the setting that makes warnings into fatal errors.
nophead wrote
If testing undef was an error it would break a lot of my code.
I often use if(list[n]) to mean if the list has a non zero nth element. If
the list doesn't have that element I get undef, which is false. I would
have to guard the test with a check of the length, which is less efficient
and more verbose.
On Mon, 27 Jul 2020 at 15:37, Ronaldo Persiano <
'undef' is a nightmare indeed. But it is not the only one: there are
'nan'
and 'inf' too which can turn into undef in the next operation. Should
they
be eradicated? Certainly not, they have their place, at least 'undef'
has.
To avoid nightmares, to have peace of mind, we need discipline. And
consistent emergence of these values in operations.
What should be the result of a polygon triangulation function if the
polygon has self-intersection? I would say that 'undef' (meaning "no
solution possible") is the best option. To generate an error or an empty
list (or some other signaling value) could be an alternative. The first
seems radical because it is so hard to find whether a polygon has
self-intersection as trying to triangulate it. The triangulation function
would be the check! The other options are falsely safe because a careless
coder may get in trouble later trying to access an element of an empty
list, for instance. There is no way to protect a careless code.That means
we need to always be careful with operations that might have "unusual"
results. That is a place for 'undef'.
Definitely an inequality expression involving pears and apples should
result in 'undef' (as we don't have a complete type order). That is
consistent with the result of trying to divide pears by a number or
access
an array component of a single value (and that doesn't mean 'undef' is
another number value or another list) (*). But, yes, a test, either in an
if() or in a ternary expression, of 'undef' should be an error. If you
don't generate the error, the propagation will be uncontrollable. That is
the devil's place. 'undef' should not be taken as 'false'. Why not allow
the 'undef' value of an inequality be set to a variable? Or checked
against
'undef' ? 'undef' is 'undef'.
(*) Dividing or multiplying a numerical vector by a scalar is a well
defined operation on linear algebra. There are good well defined
exceptions to the rule that 'pears' can't mix with 'apples'.
OpenSCAD mailing list
The efficiency argument is weak in this case. I tested the run time for
len(foo) >=1001 ? foo[1000] : -1
compared to
foo[1000] ? foo[1000] : -1
where foo had length smaller than 1001.
I ran this 10 millions times. The run time for the length check was 1
microsecond. The undef test shaved this down to 0.7 microseconds.
I cannot conceive of a way that a different of 0.3 microseconds is important
in an OpenSCAD program.
The problem of your code being broken is the serious one. Personally if I
had a body of code written that way and the option to change OpenSCAD to
produce errors with undef I'd jump for errors in a heartbeat. The amount of
time it would take me to fix a megabyte of code would be less than the time
I've wasted already debugging undefs that propagate through my code. But
the backward compatibility problem is a definite problem. Perhaps a
setting could be introduced to optionally make undefs into fatal errors,
similar to the setting that makes warnings into fatal errors.
nophead wrote
> If testing undef was an error it would break a lot of my code.
>
> I often use if(list[n]) to mean if the list has a non zero nth element. If
> the list doesn't have that element I get undef, which is false. I would
> have to guard the test with a check of the length, which is less efficient
> and more verbose.
>
>
> On Mon, 27 Jul 2020 at 15:37, Ronaldo Persiano <
> rcmpersiano@
> >
> wrote:
>
>> 'undef' is a nightmare indeed. But it is not the only one: there are
>> 'nan'
>> and 'inf' too which can turn into undef in the next operation. Should
>> they
>> be eradicated? Certainly not, they have their place, at least 'undef'
>> has.
>> To avoid nightmares, to have peace of mind, we need discipline. And
>> consistent emergence of these values in operations.
>>
>> What should be the result of a polygon triangulation function if the
>> polygon has self-intersection? I would say that 'undef' (meaning "no
>> solution possible") is the best option. To generate an error or an empty
>> list (or some other signaling value) could be an alternative. The first
>> seems radical because it is so hard to find whether a polygon has
>> self-intersection as trying to triangulate it. The triangulation function
>> would be the check! The other options are falsely safe because a careless
>> coder may get in trouble later trying to access an element of an empty
>> list, for instance. There is no way to protect a careless code.That means
>> we need to always be careful with operations that might have "unusual"
>> results. That is a place for 'undef'.
>>
>> Definitely an inequality expression involving pears and apples should
>> result in 'undef' (as we don't have a complete type order). That is
>> consistent with the result of trying to divide pears by a number or
>> access
>> an array component of a single value (and that doesn't mean 'undef' is
>> another number value or another list) (*). But, yes, a test, either in an
>> if() or in a ternary expression, of 'undef' should be an error. If you
>> don't generate the error, the propagation will be uncontrollable. That is
>> the devil's place. 'undef' should not be taken as 'false'. Why not allow
>> the 'undef' value of an inequality be set to a variable? Or checked
>> against
>> 'undef' ? *'undef' is 'undef'*.
>>
>> (*) Dividing or multiplying a numerical vector by a scalar is a well
>> defined operation on linear algebra. There are good well defined
>> exceptions to the rule that 'pears' can't mix with 'apples'.
>>
>> _______________________________________________
>> 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/
RP
Ronaldo Persiano
Mon, Jul 27, 2020 4:08 PM
The amount of
time it would take me to fix a megabyte of code would be less than the time
I've wasted already debugging undefs that propagate through my code. But
the backward compatibility problem is a definite problem. Perhaps a
setting could be introduced to optionally make undefs into fatal errors,
similar to the setting that makes warnings into fatal errors.
That already came to my mind. An alternative is to have a global variable,
like $safe_mode, that would control the system reaction to an 'undef'
result: no errors would be generated iff it is false, its default value. It
could be set locally if needed.
>
> The amount of
> time it would take me to fix a megabyte of code would be less than the time
> I've wasted already debugging undefs that propagate through my code. But
> the backward compatibility problem is a definite problem. Perhaps a
> setting could be introduced to optionally make undefs into fatal errors,
> similar to the setting that makes warnings into fatal errors.
That already came to my mind. An alternative is to have a global variable,
like $safe_mode, that would control the system reaction to an 'undef'
result: no errors would be generated iff it is false, its default value. It
could be set locally if needed.
JB
Jordan Brown
Mon, Jul 27, 2020 4:38 PM
On 7/27/2020 4:41 AM, MathLover wrote:
So if I would compare a boolean with greater or lesser operators, I expect
an error message. True is not more or less than false.
Sorting by a boolean makes sense, and the straightforward ways to sort
want to have a greater/less concept. Does that matter for OpenSCAD?Â
Don't know.
[null propagation] made programming visual basic and other languages that had this
"feature" a minefield when dealing with booleans. So please don't go that
route. I have been there, and I really do not want to go back.
Yeah... many years ago I implemented a similar concept in dBASE III. I
was adding date support, and it was clear that we needed blank dates,
which in turn meant we had to define how comparisons worked on blank
dates. My answer was to move to ternary logic, so that the result of a
comparison could be yes, no, or maybe. It ... did not turn out well.Â
It made working with blank dates difficult, and had no redeeming value.
For those familiar with dBASE III and this issue... sorry about that.
I come up with four basic options for how to do mixed-type comparisons:
- Coerce them to match. Convert everything to strings, or to numbers,
or some other scheme. "1" == 1; "true" == true.
- Mixed types are different values and so are never equal. (You could
define an ordering of the types, so that greater and less are
defined and mixed-type lists are sortable.)
- Mixed-type comparisons yield undef.
- Mixed-type comparisons are errors.
Several languages (e.g. JavaScript) use the coercion. scheme. C is
partially coercion, for numeric types and maybe a few other cases, and
errors for other.
I don't think the undef scheme offers any value. It's basically saying
that it's an error, without making the error be immediately visible. I
don't immediately come up with any value to it.
I'm not a big fan of coercion. It provides opportunities for errors to
go undetected, while providing little value.
That leaves "error" and "never equal".
"Never equal" is appealing. It is "correct", in a sense, and allows for
sortability. On the other hand, it allows errors to go undetected. On
the whole, I think the cost (undetected errors) exceeds the value
(sortability).
I guess that leaves me with a preferred answer of "error".
Special note: undef should get special attention. Comparing against
undef should always be acceptable, and should yield true if both values
are undef and false otherwise.
Now, note that that's all about comparison operators. Boolean operators
and contexts (e.g. "if") are a different question. I'm OK with either
being strict (require boolean values) or loose (define what "truth"
means for each type individually). If I was to start from zero, I'd
probably define undef and false itself as false and all other values as
true. That would let you easily say "if the value is present, then..."
and that seems more useful than having an easy way to test for zero and
the empty string. (And I'm obsessive and think the empty string is a
perfectly good string, and that an empty list is not the same thing as
no list at all.)Â But we're not starting from zero; OpenSCAD uses a
true/not-empty/non-zero style of truth and, shrug, that's OK.
On 7/27/2020 4:41 AM, MathLover wrote:
> So if I would compare a boolean with greater or lesser operators, I expect
> an error message. True is not more or less than false.
Sorting by a boolean makes sense, and the straightforward ways to sort
want to have a greater/less concept. Does that matter for OpenSCAD?Â
Don't know.
> [null propagation] made programming visual basic and other languages that had this
> "feature" a minefield when dealing with booleans. So please don't go that
> route. I have been there, and I really do not want to go back.
Yeah... many years ago I implemented a similar concept in dBASE III. I
was adding date support, and it was clear that we needed blank dates,
which in turn meant we had to define how comparisons worked on blank
dates. My answer was to move to ternary logic, so that the result of a
comparison could be yes, no, or maybe. It ... did not turn out well.Â
It made working with blank dates difficult, and had no redeeming value.
For those familiar with dBASE III and this issue... sorry about that.
I come up with four basic options for how to do mixed-type comparisons:
* Coerce them to match. Convert everything to strings, or to numbers,
or some other scheme. "1" == 1; "true" == true.
* Mixed types are different values and so are never equal. (You could
define an ordering of the types, so that greater and less are
defined and mixed-type lists are sortable.)
* Mixed-type comparisons yield undef.
* Mixed-type comparisons are errors.
Several languages (e.g. JavaScript) use the coercion. scheme. C is
partially coercion, for numeric types and maybe a few other cases, and
errors for other.
I don't think the undef scheme offers any value. It's basically saying
that it's an error, without making the error be immediately visible. I
don't immediately come up with any value to it.
I'm not a big fan of coercion. It provides opportunities for errors to
go undetected, while providing little value.
That leaves "error" and "never equal".
"Never equal" is appealing. It is "correct", in a sense, and allows for
sortability. On the other hand, it allows errors to go undetected. On
the whole, I think the cost (undetected errors) exceeds the value
(sortability).
I guess that leaves me with a preferred answer of "error".
Special note: undef should get special attention. Comparing against
undef should always be acceptable, and should yield true if both values
are undef and false otherwise.
Now, note that that's all about comparison operators. Boolean operators
and contexts (e.g. "if") are a different question. I'm OK with either
being strict (require boolean values) or loose (define what "truth"
means for each type individually). If I was to start from zero, I'd
probably define undef and false itself as false and all other values as
true. That would let you easily say "if the value is present, then..."
and that seems more useful than having an easy way to test for zero and
the empty string. (And I'm obsessive and think the empty string is a
perfectly good string, and that an empty list is not the same thing as
no list at all.)Â But we're not starting from zero; OpenSCAD uses a
true/not-empty/non-zero style of truth and, shrug, that's OK.
JB
Jordan Brown
Mon, Jul 27, 2020 4:50 PM
On 7/27/2020 6:23 AM, Ray West wrote:
Historically, There has been a lot of misunderstanding about the true
nature of boolean, by folk who should have known better. Just because
digital computers work the way they do, they bypassed they forgot
about what the ones and zeros truly represented, and manipulated them
in the ways that ones and zeros would be manipulated as normal
bits/bytes/whatever. I blame their parents.
A lot of that comes from C, and it's important to remember that C is
just a small step up from assembly language. (The ++ and -- features,
for instance, came directly from PDP-11 autoincrement and autodecrement
instructions.)
On 7/27/2020 6:23 AM, Ray West wrote:
> Historically, There has been a lot of misunderstanding about the true
> nature of boolean, by folk who should have known better. Just because
> digital computers work the way they do, they bypassed they forgot
> about what the ones and zeros truly represented, and manipulated them
> in the ways that ones and zeros would be manipulated as normal
> bits/bytes/whatever. I blame their parents.
A lot of that comes from C, and it's important to remember that C is
just a small step up from assembly language. (The ++ and -- features,
for instance, came directly from PDP-11 autoincrement and autodecrement
instructions.)
JB
Jordan Brown
Mon, Jul 27, 2020 4:53 PM
On 7/27/2020 9:08 AM, Ronaldo Persiano wrote:
That already came to my mind. An alternative is to have a global
variable, like $safe_mode, that would control the system reaction to
an 'undef' result: no errors would be generated iff it is false, its
default value. It could be set locally if needed.
$something would have the wrong scope. You want lexical scoping.
You want a file, module, or function to say "I want/don't-want safe
mode" without having that affect other unrelated files, modules, or
functions that it might call. You want to let a library say, one time
at the top, "I want safe mode".
But which is "safe", generating errors (and thus protecting you from
bugs) or not (and thus "protecting" you from errors)?
Suggest something with a more definitive meaning, e.g.
"allow_mixed_type_comparisons".
On 7/27/2020 9:08 AM, Ronaldo Persiano wrote:
> That already came to my mind. An alternative is to have a global
> variable, like $safe_mode, that would control the system reaction to
> an 'undef' result: no errors would be generated iff it is false, its
> default value. It could be set locally if needed.
$something would have the wrong scope. You want lexical scoping.
You want a file, module, or function to say "I want/don't-want safe
mode" without having that affect other unrelated files, modules, or
functions that it might call. You want to let a library say, one time
at the top, "I want safe mode".
But which is "safe", generating errors (and thus protecting you from
bugs) or not (and thus "protecting" you from errors)?
Suggest something with a more definitive meaning, e.g.
"allow_mixed_type_comparisons".
A
adrianv
Mon, Jul 27, 2020 5:22 PM
But which is "safe", generating errors (and thus protecting you from
bugs) or not (and thus "protecting" you from errors)?
Suggest something with a more definitive meaning, e.g.
"allow_mixed_type_comparisons".
Who cares about mixed type comparisons??? Does anybody ever use them? If
we banned them outright would anybody's code break? Anybody have an example
of a use for a mixed type comparison?
The topic has drifted to the broader topic of undef in general. I suggested
mixed type comparisons should return undef because that would be consistent
(and the whole issue seemed to be a case of worrying a lot about
consistency). But this leads to the question of undef in general, which is
a real problem that arises all the time. And that's what Ronaldo was
talking about. Perhaps "$return_undefs" would be a better name?
It does seem like the scoping is bit of a problem, but the simple solution
is that any (library) code that relies on computations with undefs would
need to explicitly set the variable to true anywhere that it mattered. This
seems kind of messy to me. Also there is no precedent for this type of
control variable in OpenSCAD.
It seems to me that if there's an interest in making OpenSCAD more usable it
makes more sense to make more of a break. Introduce a new version where
errors are errors, similar to some of the breaking changes made in 2019.05.
If you need to run old code, you run it in compatibility mode where your
whole program runs in the old way, similar to how you can change the setting
to make warnings into fatal errors. New code will still run in
compatibility mode. You can just work entirely in compatibility mode if
undefs don't bother you. This approach is straight forward and doesn't lead
to some complicated mess of people running code where parts of it need a
different mode than other parts. It also directly deprecates the old style
and encourages better coding practice moving forward.
Another option would be to make operations on undef issue a warning. Then
old code will still run, but if you want to you can enable the option that
makes warnings into errors.
--
Sent from: http://forum.openscad.org/
JordanBrown wrote
>
> But which is "safe", generating errors (and thus protecting you from
> bugs) or not (and thus "protecting" you from errors)?
>
> Suggest something with a more definitive meaning, e.g.
> "allow_mixed_type_comparisons".
Who cares about mixed type comparisons??? Does anybody ever use them? If
we banned them outright would anybody's code break? Anybody have an example
of a use for a mixed type comparison?
The topic has drifted to the broader topic of undef in general. I suggested
mixed type comparisons should return undef because that would be consistent
(and the whole issue seemed to be a case of worrying a lot about
consistency). But this leads to the question of undef in general, which is
a real problem that arises all the time. And that's what Ronaldo was
talking about. Perhaps "$return_undefs" would be a better name?
It does seem like the scoping is bit of a problem, but the simple solution
is that any (library) code that relies on computations with undefs would
need to explicitly set the variable to true anywhere that it mattered. This
seems kind of messy to me. Also there is no precedent for this type of
control variable in OpenSCAD.
It seems to me that if there's an interest in making OpenSCAD more usable it
makes more sense to make more of a break. Introduce a new version where
errors are errors, similar to some of the breaking changes made in 2019.05.
If you need to run old code, you run it in compatibility mode where your
whole program runs in the old way, similar to how you can change the setting
to make warnings into fatal errors. New code will still run in
compatibility mode. You can just work entirely in compatibility mode if
undefs don't bother you. This approach is straight forward and doesn't lead
to some complicated mess of people running code where parts of it need a
different mode than other parts. It also directly deprecates the old style
and encourages better coding practice moving forward.
Another option would be to make operations on undef issue a warning. Then
old code will still run, but if you want to you can enable the option that
makes warnings into errors.
--
Sent from: http://forum.openscad.org/
AC
A. Craig West
Mon, Jul 27, 2020 5:39 PM
As was mentioned by somebody, mixed type comparisons for equality are well
defined, so undef isn't really necessary for that.
On Mon, 27 Jul 2020, 13:22 adrianv, avm4@cornell.edu wrote:
But which is "safe", generating errors (and thus protecting you from
bugs) or not (and thus "protecting" you from errors)?
Suggest something with a more definitive meaning, e.g.
"allow_mixed_type_comparisons".
Who cares about mixed type comparisons??? Does anybody ever use them? If
we banned them outright would anybody's code break? Anybody have an
example
of a use for a mixed type comparison?
The topic has drifted to the broader topic of undef in general. I
suggested
mixed type comparisons should return undef because that would be consistent
(and the whole issue seemed to be a case of worrying a lot about
consistency). But this leads to the question of undef in general, which
is
a real problem that arises all the time. And that's what Ronaldo was
talking about. Perhaps "$return_undefs" would be a better name?
It does seem like the scoping is bit of a problem, but the simple solution
is that any (library) code that relies on computations with undefs would
need to explicitly set the variable to true anywhere that it mattered.
This
seems kind of messy to me. Also there is no precedent for this type of
control variable in OpenSCAD.
It seems to me that if there's an interest in making OpenSCAD more usable
it
makes more sense to make more of a break. Introduce a new version where
errors are errors, similar to some of the breaking changes made in
2019.05.
If you need to run old code, you run it in compatibility mode where your
whole program runs in the old way, similar to how you can change the
setting
to make warnings into fatal errors. New code will still run in
compatibility mode. You can just work entirely in compatibility mode if
undefs don't bother you. This approach is straight forward and doesn't
lead
to some complicated mess of people running code where parts of it need a
different mode than other parts. It also directly deprecates the old style
and encourages better coding practice moving forward.
Another option would be to make operations on undef issue a warning. Then
old code will still run, but if you want to you can enable the option that
makes warnings into errors.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
As was mentioned by somebody, mixed type comparisons for equality are well
defined, so undef isn't really necessary for that.
On Mon, 27 Jul 2020, 13:22 adrianv, <avm4@cornell.edu> wrote:
> JordanBrown wrote
> >
> > But which is "safe", generating errors (and thus protecting you from
> > bugs) or not (and thus "protecting" you from errors)?
> >
> > Suggest something with a more definitive meaning, e.g.
> > "allow_mixed_type_comparisons".
>
> Who cares about mixed type comparisons??? Does anybody ever use them? If
> we banned them outright would anybody's code break? Anybody have an
> example
> of a use for a mixed type comparison?
>
> The topic has drifted to the broader topic of undef in general. I
> suggested
> mixed type comparisons should return undef because that would be consistent
> (and the whole issue seemed to be a case of worrying a lot about
> consistency). But this leads to the question of undef in general, which
> is
> a real problem that arises all the time. And that's what Ronaldo was
> talking about. Perhaps "$return_undefs" would be a better name?
>
> It does seem like the scoping is bit of a problem, but the simple solution
> is that any (library) code that relies on computations with undefs would
> need to explicitly set the variable to true anywhere that it mattered.
> This
> seems kind of messy to me. Also there is no precedent for this type of
> control variable in OpenSCAD.
>
> It seems to me that if there's an interest in making OpenSCAD more usable
> it
> makes more sense to make more of a break. Introduce a new version where
> errors are errors, similar to some of the breaking changes made in
> 2019.05.
> If you need to run old code, you run it in compatibility mode where your
> whole program runs in the old way, similar to how you can change the
> setting
> to make warnings into fatal errors. New code will still run in
> compatibility mode. You can just work entirely in compatibility mode if
> undefs don't bother you. This approach is straight forward and doesn't
> lead
> to some complicated mess of people running code where parts of it need a
> different mode than other parts. It also directly deprecates the old style
> and encourages better coding practice moving forward.
>
> Another option would be to make operations on undef issue a warning. Then
> old code will still run, but if you want to you can enable the option that
> makes warnings into errors.
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Mon, Jul 27, 2020 6:38 PM
That we me, actually. I wasn't clear in my last post when I was talking
about comparisons. I meant relative comparisons: <, >, <= and =>. These
are not meaningful between different types (and not necessarily even for
matched types).
And just to be clear, the kinds of operations I'm talking about that produce
undef and frustrate debugging:
list[val] where val>=len(list) or val<0
x[val] where x is not a list
x+y where x and y are different types (including the case that either
argument is undef)
x*y where x and y are incompatible
and so on. All of these things should be errors.
acwest wrote
As was mentioned by somebody, mixed type comparisons for equality are well
defined, so undef isn't really necessary for that.
On Mon, 27 Jul 2020, 13:22 adrianv, <
But which is "safe", generating errors (and thus protecting you from
bugs) or not (and thus "protecting" you from errors)?
Suggest something with a more definitive meaning, e.g.
"allow_mixed_type_comparisons".
Who cares about mixed type comparisons??? Does anybody ever use them?
If
we banned them outright would anybody's code break? Anybody have an
example
of a use for a mixed type comparison?
The topic has drifted to the broader topic of undef in general. I
suggested
mixed type comparisons should return undef because that would be
consistent
(and the whole issue seemed to be a case of worrying a lot about
consistency). But this leads to the question of undef in general, which
is
a real problem that arises all the time. And that's what Ronaldo was
talking about. Perhaps "$return_undefs" would be a better name?
It does seem like the scoping is bit of a problem, but the simple
solution
is that any (library) code that relies on computations with undefs would
need to explicitly set the variable to true anywhere that it mattered.
This
seems kind of messy to me. Also there is no precedent for this type of
control variable in OpenSCAD.
It seems to me that if there's an interest in making OpenSCAD more usable
it
makes more sense to make more of a break. Introduce a new version where
errors are errors, similar to some of the breaking changes made in
2019.05.
If you need to run old code, you run it in compatibility mode where your
whole program runs in the old way, similar to how you can change the
setting
to make warnings into fatal errors. New code will still run in
compatibility mode. You can just work entirely in compatibility mode if
undefs don't bother you. This approach is straight forward and doesn't
lead
to some complicated mess of people running code where parts of it need a
different mode than other parts. It also directly deprecates the old
style
and encourages better coding practice moving forward.
Another option would be to make operations on undef issue a warning.
Then
old code will still run, but if you want to you can enable the option
that
makes warnings into errors.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
That we me, actually. I wasn't clear in my last post when I was talking
about comparisons. I meant relative comparisons: <, >, <= and =>. These
are not meaningful between different types (and not necessarily even for
matched types).
And just to be clear, the kinds of operations I'm talking about that produce
undef and frustrate debugging:
list[val] where val>=len(list) or val<0
x[val] where x is not a list
x+y where x and y are different types (including the case that either
argument is undef)
x*y where x and y are incompatible
and so on. All of these things should be errors.
acwest wrote
> As was mentioned by somebody, mixed type comparisons for equality are well
> defined, so undef isn't really necessary for that.
>
> On Mon, 27 Jul 2020, 13:22 adrianv, <
> avm4@
> > wrote:
>
>> JordanBrown wrote
>> >
>> > But which is "safe", generating errors (and thus protecting you from
>> > bugs) or not (and thus "protecting" you from errors)?
>> >
>> > Suggest something with a more definitive meaning, e.g.
>> > "allow_mixed_type_comparisons".
>>
>> Who cares about mixed type comparisons??? Does anybody ever use them?
>> If
>> we banned them outright would anybody's code break? Anybody have an
>> example
>> of a use for a mixed type comparison?
>>
>> The topic has drifted to the broader topic of undef in general. I
>> suggested
>> mixed type comparisons should return undef because that would be
>> consistent
>> (and the whole issue seemed to be a case of worrying a lot about
>> consistency). But this leads to the question of undef in general, which
>> is
>> a real problem that arises all the time. And that's what Ronaldo was
>> talking about. Perhaps "$return_undefs" would be a better name?
>>
>> It does seem like the scoping is bit of a problem, but the simple
>> solution
>> is that any (library) code that relies on computations with undefs would
>> need to explicitly set the variable to true anywhere that it mattered.
>> This
>> seems kind of messy to me. Also there is no precedent for this type of
>> control variable in OpenSCAD.
>>
>> It seems to me that if there's an interest in making OpenSCAD more usable
>> it
>> makes more sense to make more of a break. Introduce a new version where
>> errors are errors, similar to some of the breaking changes made in
>> 2019.05.
>> If you need to run old code, you run it in compatibility mode where your
>> whole program runs in the old way, similar to how you can change the
>> setting
>> to make warnings into fatal errors. New code will still run in
>> compatibility mode. You can just work entirely in compatibility mode if
>> undefs don't bother you. This approach is straight forward and doesn't
>> lead
>> to some complicated mess of people running code where parts of it need a
>> different mode than other parts. It also directly deprecates the old
>> style
>> and encourages better coding practice moving forward.
>>
>> Another option would be to make operations on undef issue a warning.
>> Then
>> old code will still run, but if you want to you can enable the option
>> that
>> makes warnings into errors.
>>
>>
>>
>>
>> --
>> Sent from: http://forum.openscad.org/
>>
>> _______________________________________________
>> 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/
M
MichaelAtOz
Tue, Jul 28, 2020 8:31 AM
The current undef behaviour allows you to handle errors.
You can detect that the result is undef and take appropriate action.
This is particularly useful when you don't have predefined data, or optional
parameters.
undef causing an error would prevent that, even a warning, it is already
problematic with warnings in for() loops or recursive code flooding the
console.
Loaded design 'C:/Users/MeB/Documents/3D-REPRAP/Things/My
Things/badge.scad'.
Parsing design (AST generation)...
WARNING: Letter_height was assigned on line 7 but was overwritten on line 78
WARNING: kh was assigned on line 8 but was overwritten on line 128
Compiling design (CSG Tree generation)...
ECHO: "OS V=20190500"
ECHO: "OSLIB/Write/write.scad"
WARNING: search term not found: "B", in file badge.scad, line 93
WARNING: search term not found: "l", in file badge.scad, line 93
WARNING: search term not found: "a", in file badge.scad, line 93
WARNING: search term not found: "c", in file badge.scad, line 93
WARNING: search term not found: "k", in file badge.scad, line 93
WARNING: search term not found: "o", in file badge.scad, line 93
WARNING: search term not found: "s", in file badge.scad, line 93
WARNING: search term not found: "e", in file badge.scad, line 93
Compiling design (CSG Products generation)...
Geometries in cache: 129
Geometry cache size in bytes: 852176
CGAL Polyhedrons in cache: 1
CGAL cache size in bytes: 1650000
Compiling design (CSG Products normalization)...
Compiling background (1 CSG Trees)...
Normalized CSG tree has 93 elements
Compile and preview finished.
Total rendering time: 0 hours, 0 minutes, 23 seconds
The first two are valid overwrites to overcome a include<> default
assignment.
The using search to not find something is perfectly valid logic, just lucky
in this case the text is short.
Admin - email* me if you need anything, or if I've done something stupid...
- click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
The current undef behaviour allows you to handle errors.
You can detect that the result is undef and take appropriate action.
This is particularly useful when you don't have predefined data, or optional
parameters.
undef causing an error would prevent that, even a warning, it is already
problematic with warnings in for() loops or recursive code flooding the
console.
-------------------------------
Loaded design 'C:/Users/MeB/Documents/3D-REPRAP/Things/My
Things/badge.scad'.
Parsing design (AST generation)...
WARNING: Letter_height was assigned on line 7 but was overwritten on line 78
WARNING: kh was assigned on line 8 but was overwritten on line 128
Compiling design (CSG Tree generation)...
ECHO: "OS V=20190500"
ECHO: "OSLIB/Write/write.scad"
WARNING: search term not found: "B", in file badge.scad, line 93
WARNING: search term not found: "l", in file badge.scad, line 93
WARNING: search term not found: "a", in file badge.scad, line 93
WARNING: search term not found: "c", in file badge.scad, line 93
WARNING: search term not found: "k", in file badge.scad, line 93
WARNING: search term not found: "o", in file badge.scad, line 93
WARNING: search term not found: "s", in file badge.scad, line 93
WARNING: search term not found: "e", in file badge.scad, line 93
Compiling design (CSG Products generation)...
Geometries in cache: 129
Geometry cache size in bytes: 852176
CGAL Polyhedrons in cache: 1
CGAL cache size in bytes: 1650000
Compiling design (CSG Products normalization)...
Compiling background (1 CSG Trees)...
Normalized CSG tree has 93 elements
Compile and preview finished.
Total rendering time: 0 hours, 0 minutes, 23 seconds
-----------------------------
The first two are valid overwrites to overcome a include<> default
assignment.
The using search to not find something is perfectly valid logic, just lucky
in this case the text is short.
-----
Admin - email* me if you need anything, or if I've done something stupid...
* click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
NH
nop head
Tue, Jul 28, 2020 8:52 AM
Overwriting a value from an include should not generate a warning. You
should only get them if you assign twice in the same file and it looks like
that is what you have.
I do find with new versions of OpenSCAD I have to add more code to get rid
of warnings. It used to be OK to have zero or negative dimensions for
primitives, they just disappeared, now I have to guard then with ifs.
It used to be OK to pass a scalar to len() and was documented to return
undef, now it gives a warning and I have to guard it with is_list() and
make my code not compatible with older versions.
There is current PR to make accesses off the end of a list a warning, so I
would have to guard all my if(list[n]) statements because I have variable
length object descriptions.
Perhaps I am just lazy but I like brief languages and it doesn't seem like
progress to have to add more and more code to do the same thing.
On Tue, 28 Jul 2020 at 09:32, MichaelAtOz oz.at.michael@gmail.com wrote:
The current undef behaviour allows you to handle errors.
You can detect that the result is undef and take appropriate action.
This is particularly useful when you don't have predefined data, or
optional
parameters.
undef causing an error would prevent that, even a warning, it is already
problematic with warnings in for() loops or recursive code flooding the
console.
Loaded design 'C:/Users/MeB/Documents/3D-REPRAP/Things/My
Things/badge.scad'.
Parsing design (AST generation)...
WARNING: Letter_height was assigned on line 7 but was overwritten on line
78
WARNING: kh was assigned on line 8 but was overwritten on line 128
Compiling design (CSG Tree generation)...
ECHO: "OS V=20190500"
ECHO: "OSLIB/Write/write.scad"
WARNING: search term not found: "B", in file badge.scad, line 93
WARNING: search term not found: "l", in file badge.scad, line 93
WARNING: search term not found: "a", in file badge.scad, line 93
WARNING: search term not found: "c", in file badge.scad, line 93
WARNING: search term not found: "k", in file badge.scad, line 93
WARNING: search term not found: "o", in file badge.scad, line 93
WARNING: search term not found: "s", in file badge.scad, line 93
WARNING: search term not found: "e", in file badge.scad, line 93
Compiling design (CSG Products generation)...
Geometries in cache: 129
Geometry cache size in bytes: 852176
CGAL Polyhedrons in cache: 1
CGAL cache size in bytes: 1650000
Compiling design (CSG Products normalization)...
Compiling background (1 CSG Trees)...
Normalized CSG tree has 93 elements
Compile and preview finished.
Total rendering time: 0 hours, 0 minutes, 23 seconds
The first two are valid overwrites to overcome a include<> default
assignment.
The using search to not find something is perfectly valid logic, just lucky
in this case the text is short.
Admin - email* me if you need anything, or if I've done something
stupid...
- click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the
Public Domain; to the extent possible under law, I have waived all
copyright and related or neighbouring rights to this work. Obviously
inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Overwriting a value from an include should not generate a warning. You
should only get them if you assign twice in the same file and it looks like
that is what you have.
I do find with new versions of OpenSCAD I have to add more code to get rid
of warnings. It used to be OK to have zero or negative dimensions for
primitives, they just disappeared, now I have to guard then with ifs.
It used to be OK to pass a scalar to len() and was documented to return
undef, now it gives a warning and I have to guard it with is_list() and
make my code not compatible with older versions.
There is current PR to make accesses off the end of a list a warning, so I
would have to guard all my if(list[n]) statements because I have variable
length object descriptions.
Perhaps I am just lazy but I like brief languages and it doesn't seem like
progress to have to add more and more code to do the same thing.
On Tue, 28 Jul 2020 at 09:32, MichaelAtOz <oz.at.michael@gmail.com> wrote:
> The current undef behaviour allows you to handle errors.
> You can detect that the result is undef and take appropriate action.
> This is particularly useful when you don't have predefined data, or
> optional
> parameters.
>
> undef causing an error would prevent that, even a warning, it is already
> problematic with warnings in for() loops or recursive code flooding the
> console.
>
> -------------------------------
>
> Loaded design 'C:/Users/MeB/Documents/3D-REPRAP/Things/My
> Things/badge.scad'.
> Parsing design (AST generation)...
> WARNING: Letter_height was assigned on line 7 but was overwritten on line
> 78
> WARNING: kh was assigned on line 8 but was overwritten on line 128
> Compiling design (CSG Tree generation)...
> ECHO: "OS V=20190500"
> ECHO: "OSLIB/Write/write.scad"
> WARNING: search term not found: "B", in file badge.scad, line 93
> WARNING: search term not found: "l", in file badge.scad, line 93
> WARNING: search term not found: "a", in file badge.scad, line 93
> WARNING: search term not found: "c", in file badge.scad, line 93
> WARNING: search term not found: "k", in file badge.scad, line 93
> WARNING: search term not found: "o", in file badge.scad, line 93
> WARNING: search term not found: "s", in file badge.scad, line 93
> WARNING: search term not found: "e", in file badge.scad, line 93
> Compiling design (CSG Products generation)...
> Geometries in cache: 129
> Geometry cache size in bytes: 852176
> CGAL Polyhedrons in cache: 1
> CGAL cache size in bytes: 1650000
> Compiling design (CSG Products normalization)...
> Compiling background (1 CSG Trees)...
> Normalized CSG tree has 93 elements
> Compile and preview finished.
> Total rendering time: 0 hours, 0 minutes, 23 seconds
>
> -----------------------------
>
> The first two are valid overwrites to overcome a include<> default
> assignment.
> The using search to not find something is perfectly valid logic, just lucky
> in this case the text is short.
>
>
>
> -----
> Admin - email* me if you need anything, or if I've done something
> stupid...
>
> * click on my MichaelAtOz label, there is a link to email me.
>
> Unless specifically shown otherwise above, my contribution is in the
> Public Domain; to the extent possible under law, I have waived all
> copyright and related or neighbouring rights to this work. Obviously
> inclusion of works of previous authors is not included in the above.
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
M
MichaelAtOz
Tue, Jul 28, 2020 9:09 AM
Overwriting a value from an include should not generate a warning. You
should only get them if you assign twice in the same file and it looks
like
that is what you have.
If I want to overwrite the default var 't' which is set in the include<>.
with an expression in the main program, such as
t=Letter_height+kh-0.1;
then Letter_height & kh must be in scope before the include<>, hence they
need to be assigned.
But they are assigned real values after the include<> in the main body of a
customizer, after the user selects a value.
Admin - email* me if you need anything, or if I've done something stupid...
- click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
nophead wrote
> Overwriting a value from an include should not generate a warning. You
> should only get them if you assign twice in the same file and it looks
> like
> that is what you have.
If I want to overwrite the default var 't' which is set in the include<>.
with an expression in the main program, such as
t=Letter_height+kh-0.1;
then Letter_height & kh must be in scope before the include<>, hence they
need to be assigned.
But they are assigned real values after the include<> in the main body of a
customizer, after the user selects a value.
-----
Admin - email* me if you need anything, or if I've done something stupid...
* click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
A
adrianv
Tue, Jul 28, 2020 10:44 AM
I think you're missing the point. The current undef behavior may allow me to
HANDLE errors but it does not enable me to find them. Or rather, it makes
finding them extremely time consuming.
To write code that handles errors as you say is absurdly verbose:
foo = a+b;
assert(foo!=undef); // in case a or b are mixed type
bar = foo-c;
assert(bar!=undef); // c might be the wrong type
x=somelist[ind];
assert(x!=undef); // ind might be out of bounds
etc. Every time an instruction might produce undef I need an assert to
check for the possible error. If I don't do this (and indeed I do not) then
the undefs propagate through the code until somewhere later, a fatal error
occurs---or maybe no error occurs but the final output is missing or wrong.
So as a practical example, a user reported that something broke in the
library I work on. I took a look and the function in question was producing
undef. Why? Well, because the function it called produced undef. But why?
Because it called a function that produced undef. Why did that happen...and
so it goes. Eventually after inserting many echo statements I am finally
able to identify the statement that originated the undef, and hence
understand the bug. So it took me 15 minutes to diagnose an issue that in
a language without propagating undef would have taken 15 seconds. Yes, it's
possible to write code that checks all the parameters and ensures
compatibility of args so that undefs don't occur. I do this, in fact. So
in bug free code undef works as an error handling mechanism. But in code
that might have bugs---say I forget a check, or I screw up and check a list
against the wrong length---undef is a mess because it doesn't give
information about where the error occurred.
I'm not sure what is going on with your example below. Is search() being
changed to produce a warning when it doesn't find anything? That would be
stupid. Right now it returns an empty list.
Yes, undef is useful when you have optional parameters. The problem with
undef is not that it exists but that operations that are invalid produce it
as output instead of producing an immediate error. Undef as a concept is
OK for unset parameters, for example. What's not OK is that a+b or
list[foo] can produce undef as output.
I'm not quite sure what it has to do with undef, but why write code where
you need to change variables? I would write the library so that this wasn't
necessary. Passing data with global variables isn't good coding practice,
but if it must be done for some reason you could still do it in such a way
that you don't have to redefine a variable and hende don't get a warning.
MichaelAtOz wrote
The current undef behaviour allows you to handle errors.
You can detect that the result is undef and take appropriate action.
This is particularly useful when you don't have predefined data, or
optional
parameters.
undef causing an error would prevent that, even a warning, it is already
problematic with warnings in for() loops or recursive code flooding the
console.
Loaded design 'C:/Users/MeB/Documents/3D-REPRAP/Things/My
Things/badge.scad'.
Parsing design (AST generation)...
WARNING: Letter_height was assigned on line 7 but was overwritten on line
78
WARNING: kh was assigned on line 8 but was overwritten on line 128
Compiling design (CSG Tree generation)...
ECHO: "OS V=20190500"
ECHO: "OSLIB/Write/write.scad"
WARNING: search term not found: "B", in file badge.scad, line 93
WARNING: search term not found: "l", in file badge.scad, line 93
WARNING: search term not found: "a", in file badge.scad, line 93
WARNING: search term not found: "c", in file badge.scad, line 93
WARNING: search term not found: "k", in file badge.scad, line 93
WARNING: search term not found: "o", in file badge.scad, line 93
WARNING: search term not found: "s", in file badge.scad, line 93
WARNING: search term not found: "e", in file badge.scad, line 93
Compiling design (CSG Products generation)...
Geometries in cache: 129
Geometry cache size in bytes: 852176
CGAL Polyhedrons in cache: 1
CGAL cache size in bytes: 1650000
Compiling design (CSG Products normalization)...
Compiling background (1 CSG Trees)...
Normalized CSG tree has 93 elements
Compile and preview finished.
Total rendering time: 0 hours, 0 minutes, 23 seconds
The first two are valid overwrites to overcome a include<> default
assignment.
The using search to not find something is perfectly valid logic, just
lucky
in this case the text is short.
Admin - email* me if you need anything, or if I've done something
stupid...
- click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the
Public Domain; to the extent possible under law, I have waived all
copyright and related or neighbouring rights to this work. Obviously
inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
I think you're missing the point. The current undef behavior may allow me to
HANDLE errors but it does not enable me to find them. Or rather, it makes
finding them extremely time consuming.
To write code that handles errors as you say is absurdly verbose:
foo = a+b;
assert(foo!=undef); // in case a or b are mixed type
bar = foo-c;
assert(bar!=undef); // c might be the wrong type
x=somelist[ind];
assert(x!=undef); // ind might be out of bounds
etc. Every time an instruction might produce undef I need an assert to
check for the possible error. If I don't do this (and indeed I do not) then
the undefs propagate through the code until somewhere later, a fatal error
occurs---or maybe no error occurs but the final output is missing or wrong.
So as a practical example, a user reported that something broke in the
library I work on. I took a look and the function in question was producing
undef. Why? Well, because the function it called produced undef. But why?
Because it called a function that produced undef. Why did that happen...and
so it goes. Eventually after inserting many echo statements I am finally
able to identify the statement that originated the undef, and hence
understand the bug. So it took me 15 minutes to diagnose an issue that in
a language without propagating undef would have taken 15 seconds. Yes, it's
possible to write code that checks all the parameters and ensures
compatibility of args so that undefs don't occur. I do this, in fact. So
in bug free code undef works as an error handling mechanism. But in code
that might have bugs---say I forget a check, or I screw up and check a list
against the wrong length---undef is a mess because it doesn't give
information about where the error occurred.
I'm not sure what is going on with your example below. Is search() being
changed to produce a warning when it doesn't find anything? That would be
stupid. Right now it returns an empty list.
Yes, undef is useful when you have optional parameters. The problem with
undef is not that it exists but that operations that are invalid produce it
as output instead of producing an immediate error. Undef as a concept is
OK for unset parameters, for example. What's not OK is that a+b or
list[foo] can produce undef as output.
I'm not quite sure what it has to do with undef, but why write code where
you need to change variables? I would write the library so that this wasn't
necessary. Passing data with global variables isn't good coding practice,
but if it must be done for some reason you could still do it in such a way
that you don't have to redefine a variable and hende don't get a warning.
MichaelAtOz wrote
> The current undef behaviour allows you to handle errors.
> You can detect that the result is undef and take appropriate action.
> This is particularly useful when you don't have predefined data, or
> optional
> parameters.
>
> undef causing an error would prevent that, even a warning, it is already
> problematic with warnings in for() loops or recursive code flooding the
> console.
>
> -------------------------------
>
> Loaded design 'C:/Users/MeB/Documents/3D-REPRAP/Things/My
> Things/badge.scad'.
> Parsing design (AST generation)...
> WARNING: Letter_height was assigned on line 7 but was overwritten on line
> 78
> WARNING: kh was assigned on line 8 but was overwritten on line 128
> Compiling design (CSG Tree generation)...
> ECHO: "OS V=20190500"
> ECHO: "OSLIB/Write/write.scad"
> WARNING: search term not found: "B", in file badge.scad, line 93
> WARNING: search term not found: "l", in file badge.scad, line 93
> WARNING: search term not found: "a", in file badge.scad, line 93
> WARNING: search term not found: "c", in file badge.scad, line 93
> WARNING: search term not found: "k", in file badge.scad, line 93
> WARNING: search term not found: "o", in file badge.scad, line 93
> WARNING: search term not found: "s", in file badge.scad, line 93
> WARNING: search term not found: "e", in file badge.scad, line 93
> Compiling design (CSG Products generation)...
> Geometries in cache: 129
> Geometry cache size in bytes: 852176
> CGAL Polyhedrons in cache: 1
> CGAL cache size in bytes: 1650000
> Compiling design (CSG Products normalization)...
> Compiling background (1 CSG Trees)...
> Normalized CSG tree has 93 elements
> Compile and preview finished.
> Total rendering time: 0 hours, 0 minutes, 23 seconds
>
> -----------------------------
>
> The first two are valid overwrites to overcome a include<> default
> assignment.
> The using search to not find something is perfectly valid logic, just
> lucky
> in this case the text is short.
>
>
>
> -----
> Admin - email* me if you need anything, or if I've done something
> stupid...
>
> * click on my MichaelAtOz label, there is a link to email me.
>
> Unless specifically shown otherwise above, my contribution is in the
> Public Domain; to the extent possible under law, I have waived all
> copyright and related or neighbouring rights to this work. Obviously
> inclusion of works of previous authors is not included in the above.
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@.openscad
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
Sent from: http://forum.openscad.org/
M
MichaelAtOz
Tue, Jul 28, 2020 11:57 AM
To write code that handles errors as you say is absurdly verbose:
no not to handle programmed errors, but data input errors, where parameter
input is allowed to be 'difficult' and that the library can detect than and
do something appropriate.
I'm not quite sure what it has to do with undef, but why write code where
you need to change variables? I would write the library so that this
wasn't
necessary. Passing data with global variables isn't good coding
practice,
but if it must be done for some reason you could still do it in such a way
that you don't have to redefine a variable and hende don't get a warning.
That is a case of someone elses library, but that situation is common, if
you have defaults in an include<> and you want to change then and need to
use top level variables, that is then only way.
Anyway, I'm pretty sure we code differently and won't agree.
Admin - email* me if you need anything, or if I've done something stupid...
- click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
adrianv wrote
> To write code that handles errors as you say is absurdly verbose:
no not to handle programmed errors, but data input errors, where parameter
input is allowed to be 'difficult' and that the library can detect than and
do something appropriate.
> I'm not quite sure what it has to do with undef, but why write code where
> you need to change variables? I would write the library so that this
> wasn't
> necessary. Passing data with global variables isn't good coding
> practice,
> but if it must be done for some reason you could still do it in such a way
> that you don't have to redefine a variable and hende don't get a warning.
That is a case of someone elses library, but that situation is common, if
you have defaults in an include<> and you want to change then and need to
use top level variables, that is then only way.
Anyway, I'm pretty sure we code differently and won't agree.
-----
Admin - email* me if you need anything, or if I've done something stupid...
* click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the Public Domain; to the extent possible under law, I have waived all copyright and related or neighbouring rights to this work. Obviously inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
DM
Doug Moen
Tue, Jul 28, 2020 1:01 PM
@MichaelAtOz: In my language, I only report errors in the situations where the majority of programming languages would report an error. Examples include: performing a boolean operation on a non-bool, or a numeric operation on a non-number, or dividing by zero.
Most languages have an operation that lets you test if a list contains a specified value (without getting an error if the value isn't found). For example, in Python you can write 'x in list' and it returns False if x is not an element of list. In OpenSCAD, you accomplish the same thing using search(x,list)!=[]
. In Javascript, list.indexOf(x)
will return the index of x in list, or -1 if not found.
The search
function shouldn't report an error if the element isn't found, and it shouldn't print warning messages. If you don't check the result of search
to see if it is [], and you try to use the non-existent first element of the search result, THEN you should get an error.
On Tue, Jul 28, 2020, at 4:31 AM, MichaelAtOz wrote:
The current undef behaviour allows you to handle errors.
You can detect that the result is undef and take appropriate action.
This is particularly useful when you don't have predefined data, or optional
parameters.
undef causing an error would prevent that, even a warning, it is already
problematic with warnings in for() loops or recursive code flooding the
console.
Loaded design 'C:/Users/MeB/Documents/3D-REPRAP/Things/My
Things/badge.scad'.
Parsing design (AST generation)...
WARNING: Letter_height was assigned on line 7 but was overwritten on line 78
WARNING: kh was assigned on line 8 but was overwritten on line 128
Compiling design (CSG Tree generation)...
ECHO: "OS V=20190500"
ECHO: "OSLIB/Write/write.scad"
WARNING: search term not found: "B", in file badge.scad, line 93
WARNING: search term not found: "l", in file badge.scad, line 93
WARNING: search term not found: "a", in file badge.scad, line 93
WARNING: search term not found: "c", in file badge.scad, line 93
WARNING: search term not found: "k", in file badge.scad, line 93
WARNING: search term not found: "o", in file badge.scad, line 93
WARNING: search term not found: "s", in file badge.scad, line 93
WARNING: search term not found: "e", in file badge.scad, line 93
Compiling design (CSG Products generation)...
Geometries in cache: 129
Geometry cache size in bytes: 852176
CGAL Polyhedrons in cache: 1
CGAL cache size in bytes: 1650000
Compiling design (CSG Products normalization)...
Compiling background (1 CSG Trees)...
Normalized CSG tree has 93 elements
Compile and preview finished.
Total rendering time: 0 hours, 0 minutes, 23 seconds
The first two are valid overwrites to overcome a include<> default
assignment.
The using search to not find something is perfectly valid logic, just lucky
in this case the text is short.
Admin - email* me if you need anything, or if I've done something stupid...
- click on my MichaelAtOz label, there is a link to email me.
Unless specifically shown otherwise above, my contribution is in the
Public Domain; to the extent possible under law, I have waived all
copyright and related or neighbouring rights to this work. Obviously
inclusion of works of previous authors is not included in the above.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
@MichaelAtOz: In my language, I only report errors in the situations where the majority of programming languages would report an error. Examples include: performing a boolean operation on a non-bool, or a numeric operation on a non-number, or dividing by zero.
Most languages have an operation that lets you test if a list contains a specified value (without getting an error if the value isn't found). For example, in Python you can write 'x in list' and it returns False if x is not an element of list. In OpenSCAD, you accomplish the same thing using `search(x,list)!=[]`. In Javascript, `list.indexOf(x)` will return the index of x in list, or -1 if not found.
The `search` function shouldn't report an error if the element isn't found, and it shouldn't print warning messages. If you don't check the result of `search` to see if it is [], and you try to use the non-existent first element of the search result, THEN you should get an error.
On Tue, Jul 28, 2020, at 4:31 AM, MichaelAtOz wrote:
> The current undef behaviour allows you to handle errors.
> You can detect that the result is undef and take appropriate action.
> This is particularly useful when you don't have predefined data, or optional
> parameters.
>
> undef causing an error would prevent that, even a warning, it is already
> problematic with warnings in for() loops or recursive code flooding the
> console.
>
> -------------------------------
>
> Loaded design 'C:/Users/MeB/Documents/3D-REPRAP/Things/My
> Things/badge.scad'.
> Parsing design (AST generation)...
> WARNING: Letter_height was assigned on line 7 but was overwritten on line 78
> WARNING: kh was assigned on line 8 but was overwritten on line 128
> Compiling design (CSG Tree generation)...
> ECHO: "OS V=20190500"
> ECHO: "OSLIB/Write/write.scad"
> WARNING: search term not found: "B", in file badge.scad, line 93
> WARNING: search term not found: "l", in file badge.scad, line 93
> WARNING: search term not found: "a", in file badge.scad, line 93
> WARNING: search term not found: "c", in file badge.scad, line 93
> WARNING: search term not found: "k", in file badge.scad, line 93
> WARNING: search term not found: "o", in file badge.scad, line 93
> WARNING: search term not found: "s", in file badge.scad, line 93
> WARNING: search term not found: "e", in file badge.scad, line 93
> Compiling design (CSG Products generation)...
> Geometries in cache: 129
> Geometry cache size in bytes: 852176
> CGAL Polyhedrons in cache: 1
> CGAL cache size in bytes: 1650000
> Compiling design (CSG Products normalization)...
> Compiling background (1 CSG Trees)...
> Normalized CSG tree has 93 elements
> Compile and preview finished.
> Total rendering time: 0 hours, 0 minutes, 23 seconds
>
> -----------------------------
>
> The first two are valid overwrites to overcome a include<> default
> assignment.
> The using search to not find something is perfectly valid logic, just lucky
> in this case the text is short.
>
>
>
> -----
> Admin - email* me if you need anything, or if I've done something stupid...
>
> * click on my MichaelAtOz label, there is a link to email me.
>
> Unless specifically shown otherwise above, my contribution is in the
> Public Domain; to the extent possible under law, I have waived all
> copyright and related or neighbouring rights to this work. Obviously
> inclusion of works of previous authors is not included in the above.
>
> --
> 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
Tue, Jul 28, 2020 8:04 PM
This is all part of an eternal debate in software design:Â when there's
a situation that might represent a bug, do you call it to the
developer's attention or do you try to soldier on? If you're going to
fail, how "hard" do you fail?
There is definitely ease of use in "just soldier on". Often a program
can silently ignore "error" conditions and produce the intended result,
without the programmer having to take explicit action to handle an
unusual condition. Often even if the result is not "correct" it is
still "close enough".
On the other hand, the more distance there is between a bug and when
it's detected, the harder it will be to find the bug.
Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
middle ground, but because the failure can easily propagate outward a
large distance it's only a bit better than not failing at all. Nobody
is going to check for that kind of error on every operation that can
produce one.
I'm pretty solidly in the "fail hard as early as possible" camp. It's
sometimes a pain in the neck to work through the "unnecessary" errors,
but the result is more confidence that the results are correct.
This is all part of an eternal debate in software design:Â when there's
a situation that might represent a bug, do you call it to the
developer's attention or do you try to soldier on? If you're going to
fail, how "hard" do you fail?
There is definitely ease of use in "just soldier on". Often a program
can silently ignore "error" conditions and produce the intended result,
without the programmer having to take explicit action to handle an
unusual condition. Often even if the result is not "correct" it is
still "close enough".
On the other hand, the more distance there is between a bug and when
it's detected, the harder it will be to find the bug.
Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
middle ground, but because the failure can easily propagate outward a
large distance it's only a bit better than not failing at all. Nobody
is going to check for that kind of error on every operation that can
produce one.
I'm pretty solidly in the "fail hard as early as possible" camp. It's
sometimes a pain in the neck to work through the "unnecessary" errors,
but the result is more confidence that the results are correct.
JW
Jan Wieck
Tue, Jul 28, 2020 8:33 PM
I'm pretty solidly in the "fail hard as early as possible" camp. It's
sometimes a pain in the neck to work through the "unnecessary" errors, but
the result is more confidence that the results are correct.
Yupp, -Werror all the way. But that is just for those who really like "no
warnings missed".
Jan
--
Jan Wieck
Principal Database Engineer
On Tue, Jul 28, 2020 at 4:05 PM Jordan Brown <openscad@jordan.maileater.net>
wrote:
> I'm pretty solidly in the "fail hard as early as possible" camp. It's
> sometimes a pain in the neck to work through the "unnecessary" errors, but
> the result is more confidence that the results are correct.
>
Yupp, -Werror all the way. But that is just for those who really like "no
warnings missed".
Jan
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
--
Jan Wieck
Principal Database Engineer
J
jon
Tue, Jul 28, 2020 8:38 PM
I agree with Jordan.
Could there be a setting to choose between "fail early" and "fail barely"?
Jon
On 7/28/2020 4:04 PM, Jordan Brown wrote:
This is all part of an eternal debate in software design:Â when
there's a situation that might represent a bug, do you call it to the
developer's attention or do you try to soldier on? If you're going to
fail, how "hard" do you fail?
There is definitely ease of use in "just soldier on". Often a program
can silently ignore "error" conditions and produce the intended
result, without the programmer having to take explicit action to
handle an unusual condition. Often even if the result is not
"correct" it is still "close enough".
On the other hand, the more distance there is between a bug and when
it's detected, the harder it will be to find the bug.
Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
middle ground, but because the failure can easily propagate outward a
large distance it's only a bit better than not failing at all. Nobody
is going to check for that kind of error on every operation that can
produce one.
I'm pretty solidly in the "fail hard as early as possible" camp. It's
sometimes a pain in the neck to work through the "unnecessary" errors,
but the result is more confidence that the results are correct.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
I agree with Jordan.
Could there be a setting to choose between "fail early" and "fail barely"?
Jon
On 7/28/2020 4:04 PM, Jordan Brown wrote:
> This is all part of an eternal debate in software design:Â when
> there's a situation that might represent a bug, do you call it to the
> developer's attention or do you try to soldier on? If you're going to
> fail, how "hard" do you fail?
>
> There is definitely ease of use in "just soldier on". Often a program
> can silently ignore "error" conditions and produce the intended
> result, without the programmer having to take explicit action to
> handle an unusual condition. Often even if the result is not
> "correct" it is still "close enough".
>
> On the other hand, the more distance there is between a bug and when
> it's detected, the harder it will be to find the bug.
>
> Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
> middle ground, but because the failure can easily propagate outward a
> large distance it's only a bit better than not failing at all. Nobody
> is going to check for that kind of error on every operation that can
> produce one.
>
> I'm pretty solidly in the "fail hard as early as possible" camp. It's
> sometimes a pain in the neck to work through the "unnecessary" errors,
> but the result is more confidence that the results are correct.
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
HL
Hans L
Wed, Jul 29, 2020 12:02 AM
Hello everyone,
I felt I should give an update on the status of this hot topic.
I haven't had time to respond directly to all the posts here, but I want to
stress that I am taking all your concerns quite seriously and am currently
working to "right the wrongs" that I made in the previous change.
In my last response to this thread I was arguing that we shouldn't return
more undefs from comparing different types, but my stance has changed on
that.
I do believe it makes sense for the language to declare certain types of
operations as "undefined", by returning undef (as it already does in many
cases, and now for the issue of comparisons between number and bools, or
basically any other mixed types).
The only problem with that, as others have already pointed out, is that
OpenSCAD is doing this silently, making it hard to trace down the source of
such programming errors. So I think this would be ok as long as we can
generate a warning from the source of the problem (whichever operation
returns undef), before these values travel further throughout the script.
Treating this as a full Type Error which halts execution is not really an
option as I don't want to break compatibility of older scripts in a major
way, but there is already an option for users who would prefer this
stricter behavior in Preferences -> Advanced -> "Stop on the first
Warning". (that's basically our "-Werror")
There is actually some difficulty involved in reporting useful
errors/warnings from deep in the code which handles this operator logic
(src/value.cc), because AST information (source file and line number) is
not passed down into these low level operators.
But I think I have a solution to this which will make it possible to do
this for comparison operators. I am still working on implementing and
testing it but as a quick summary:
- undef is currently an "empty type" , which can be used as a yes/no flag
based on its type, but contains no other data.
- I would change this type to hold a message string for why it was set
to undef. This way higher level code with access to the AST info can print
a nice warning with line numbers in addition to whatever message the low
level operator provides.
Also with such a change, it should be much easier to implement a wider
variety of "Undefined Operation" types of warnings throughout the program,
just about anywhere that undef is returned.
Lastly, since some users may not want to look at a bunch of new warnings
on scripts that are otherwise functioning fine for them, I'm considering
making these additional "Undefined Operation" warnings as a separate
user-configurable option to enable/disable.
As a first step I decided to try returning undef from comparison operators
on mixed types (no new warnings yet) and see how much of our existing
test-suite would break. The code change actually wasn't too involved,
and was done in an hour or so.
Surprisingly only two test results required updating:
-
'operator-tests.scad' (this checks all kinds of combinations of mixed
type comparisons, and was expected to change significantly)
-
'expression-precedence-tests.scad' Where the expected result for this
one line of nonsense code changed: "echo(assoc_left_ltgt = 3 < 4 > 5);"
Previously this evaluated to false, and with changes it is now more
appropriately undef.
This seems to bode well in terms of maintaining backwards compatibility,
mostly since undef will continue to evaluate to false in a boolean context.
The current Pull Request is here, if anyone wants to look it over:
https://github.com/openscad/openscad/pull/3383 Again, its still work in
progress and I will update as soon as I have some added warnings working,
but I welcome any feedback.
You can also try downloading one of the artifacts from the build servers to
see if this sort of change would impact any of your code, such as this
AppImage from CircleCI here:
https://5597-1049088-gh.circle-artifacts.com/0/64-bit/OpenSCAD-2020.07.27.ai5597-_PR33836d0547b-x86_64.AppImage
I am hopeful that this issue can be resolved in a way that is acceptable to
everyone, and even result in OpenSCAD becoming much stronger for it.
On Tue, Jul 28, 2020 at 3:38 PM jon jon@jonbondy.com wrote:
I agree with Jordan.
Could there be a setting to choose between "fail early" and "fail barely"?
Jon
On 7/28/2020 4:04 PM, Jordan Brown wrote:
This is all part of an eternal debate in software design: when there's a
situation that might represent a bug, do you call it to the developer's
attention or do you try to soldier on? If you're going to fail, how "hard"
do you fail?
There is definitely ease of use in "just soldier on". Often a program can
silently ignore "error" conditions and produce the intended result, without
the programmer having to take explicit action to handle an unusual
condition. Often even if the result is not "correct" it is still "close
enough".
On the other hand, the more distance there is between a bug and when it's
detected, the harder it will be to find the bug.
Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
middle ground, but because the failure can easily propagate outward a large
distance it's only a bit better than not failing at all. Nobody is going
to check for that kind of error on every operation that can produce one.
I'm pretty solidly in the "fail hard as early as possible" camp. It's
sometimes a pain in the neck to work through the "unnecessary" errors, but
the result is more confidence that the results are correct.
OpenSCAD mailing listDiscuss@lists.openscad.orghttp://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Hello everyone,
I felt I should give an update on the status of this hot topic.
I haven't had time to respond directly to all the posts here, but I want to
stress that I am taking all your concerns quite seriously and am currently
working to "right the wrongs" that I made in the previous change.
In my last response to this thread I was arguing that we *shouldn't* return
more undefs from comparing different types, but my stance has changed on
that.
I do believe it makes sense for the language to declare certain types of
operations as "undefined", by returning undef (as it already does in many
cases, and now for the issue of comparisons between number and bools, or
basically any other mixed types).
The only problem with that, as others have already pointed out, is that
OpenSCAD is doing this silently, making it hard to trace down the source of
such programming errors. So I think this would be ok as long as we can
generate a warning from the source of the problem (whichever operation
returns undef), before these values travel further throughout the script.
Treating this as a full Type Error which halts execution is not really an
option as I don't want to break compatibility of older scripts in a major
way, but there is already an option for users who would prefer this
stricter behavior in Preferences -> Advanced -> "Stop on the first
Warning". (that's basically our "-Werror")
There is actually some difficulty involved in reporting useful
errors/warnings from deep in the code which handles this operator logic
(src/value.cc), because AST information (source file and line number) is
not passed down into these low level operators.
But I think I have a solution to this which will make it possible to do
this for comparison operators. I am still working on implementing and
testing it but as a quick summary:
- undef is currently an "empty type" , which can be used as a yes/no flag
based on its type, but contains no other data.
- I would change this type to hold a message string for *why* it was set
to undef. This way higher level code with access to the AST info can print
a nice warning with line numbers in addition to whatever message the low
level operator provides.
Also with such a change, it should be much easier to implement a wider
variety of "Undefined Operation" types of warnings throughout the program,
just about anywhere that undef is returned.
Lastly, since some users may not want to look at a bunch of new warnings
on scripts that are otherwise functioning fine for them, I'm considering
making these additional "Undefined Operation" warnings as a separate
user-configurable option to enable/disable.
As a first step I decided to try returning undef from comparison operators
on mixed types (no new warnings yet) and see how much of our existing
test-suite would break. The code change actually wasn't too involved,
and was done in an hour or so.
Surprisingly only two test results required updating:
1) 'operator-tests.scad' (this checks all kinds of combinations of mixed
type comparisons, and was expected to change significantly)
2) 'expression-precedence-tests.scad' Where the expected result for this
one line of nonsense code changed: "echo(assoc_left_ltgt = 3 < 4 > 5);"
Previously this evaluated to false, and with changes it is now more
appropriately undef.
This seems to bode well in terms of maintaining backwards compatibility,
mostly since undef will continue to evaluate to false in a boolean context.
The current Pull Request is here, if anyone wants to look it over:
https://github.com/openscad/openscad/pull/3383 Again, its still work in
progress and I will update as soon as I have some added warnings working,
but I welcome any feedback.
You can also try downloading one of the artifacts from the build servers to
see if this sort of change would impact any of your code, such as this
AppImage from CircleCI here:
https://5597-1049088-gh.circle-artifacts.com/0/64-bit/OpenSCAD-2020.07.27.ai5597-_PR33836d0547b-x86_64.AppImage
I am hopeful that this issue can be resolved in a way that is acceptable to
everyone, and even result in OpenSCAD becoming much stronger for it.
On Tue, Jul 28, 2020 at 3:38 PM jon <jon@jonbondy.com> wrote:
> I agree with Jordan.
>
> Could there be a setting to choose between "fail early" and "fail barely"?
>
> Jon
> On 7/28/2020 4:04 PM, Jordan Brown wrote:
>
> This is all part of an eternal debate in software design: when there's a
> situation that might represent a bug, do you call it to the developer's
> attention or do you try to soldier on? If you're going to fail, how "hard"
> do you fail?
>
> There is definitely ease of use in "just soldier on". Often a program can
> silently ignore "error" conditions and produce the intended result, without
> the programmer having to take explicit action to handle an unusual
> condition. Often even if the result is not "correct" it is still "close
> enough".
>
> On the other hand, the more distance there is between a bug and when it's
> detected, the harder it will be to find the bug.
>
> Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
> middle ground, but because the failure can easily propagate outward a large
> distance it's only a bit better than not failing at all. Nobody is going
> to check for that kind of error on every operation that can produce one.
>
> I'm pretty solidly in the "fail hard as early as possible" camp. It's
> sometimes a pain in the neck to work through the "unnecessary" errors, but
> the result is more confidence that the results are correct.
>
>
> _______________________________________________
> OpenSCAD mailing listDiscuss@lists.openscad.orghttp://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
JB
Jordan Brown
Wed, Jul 29, 2020 5:04 AM
On 7/28/2020 5:02 PM, Hans L wrote:
 - I would change this type to hold a message string for why it was
set to undef. This way higher level code with access to the AST info
can print a nice warning with line numbers in addition to whatever
message the low level operator provides.
That's a very clever idea.
On 7/28/2020 5:02 PM, Hans L wrote:
> Â - I would change this type to hold a message string for *why* it was
> set to undef. This way higher level code with access to the AST info
> can print a nice warning with line numbers in addition to whatever
> message the low level operator provides.
That's a very clever idea.
RW
Rogier Wolff
Wed, Jul 29, 2020 7:10 AM
On Tue, Jul 28, 2020 at 08:04:54PM +0000, Jordan Brown wrote:
's detected, the harder it will be to find the bug.
Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
Some "failures" are not failures, and need to be handled by higher
layers. The classical example is of a program going to write a file.
To prevent accidentally overwriting the file, the program will attempt
to open the file, get an error: "Can't open file", and then proceed to
write the file.
Sure you can find other ways to do this specific thing, but this is an
example where what the "open" function thought was an error, was
precisely the opposite to the program making the call.
Moving to the context of openscad: Suppose I have a module to make a
screw that takes the "head" as an argument. You could make such a
module to take a number to define what head to use out of a list of
possible heads, but lets say I've structured this differently and I
want to pass the object. (Not sure how to do that in openscad, but
lets assume I can.... Oh! I can do it with a "children" construct!)
Now what if instead of a screw I want to make a threaded rod? No head!
In this case, the module might try to access the empty list of "heads
to put on", but that's not an error. If you report the error, HARD, as
in throw an error and abort, then I can't even create that threaded
rod.
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But
when you think about it, it is not as clear cut as you might think.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
On Tue, Jul 28, 2020 at 08:04:54PM +0000, Jordan Brown wrote:
's detected, the harder it will be to find the bug.
>
> Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
Some "failures" are not failures, and need to be handled by higher
layers. The classical example is of a program going to write a file.
To prevent accidentally overwriting the file, the program will attempt
to open the file, get an error: "Can't open file", and then proceed to
write the file.
Sure you can find other ways to do this specific thing, but this is an
example where what the "open" function thought was an error, was
precisely the opposite to the program making the call.
Moving to the context of openscad: Suppose I have a module to make a
screw that takes the "head" as an argument. You could make such a
module to take a number to define what head to use out of a list of
possible heads, but lets say I've structured this differently and I
want to pass the object. (Not sure how to do that in openscad, but
lets assume I can.... Oh! I can do it with a "children" construct!)
Now what if instead of a screw I want to make a threaded rod? No head!
In this case, the module might try to access the empty list of "heads
to put on", but that's not an error. If you report the error, HARD, as
in throw an error and abort, then I can't even create that threaded
rod.
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But
when you think about it, it is not as clear cut as you might think.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110 **
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
NH
nop head
Wed, Jul 29, 2020 8:00 AM
Yes making warmings optional doesn't really solve the backward
compatibility option. A library maintainer would want to turn it on to make
the library easy to use but would then have to fix all the intended uses of
functions returning undef by adding extra code.
Perhaps it should only be an error to use undef in operations that need a
concrete value, i.e. nearly everywhere, except in a boolean context, where
it is simply false. If the undef value carries information of what
generated it that could be part of the warning when it was used.
So something like this.
list = [];
x = list[0]; // x = undef(list index out of bounds)
if(x)
sphere(x); // Not executed
else
cube(x); // Error cube size parameter is undefined (list index out of
bounds at line 2)
On Wed, 29 Jul 2020 at 08:11, Rogier Wolff R.E.Wolff@bitwizard.nl wrote:
On Tue, Jul 28, 2020 at 08:04:54PM +0000, Jordan Brown wrote:
's detected, the harder it will be to find the bug.
Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
Some "failures" are not failures, and need to be handled by higher
layers. The classical example is of a program going to write a file.
To prevent accidentally overwriting the file, the program will attempt
to open the file, get an error: "Can't open file", and then proceed to
write the file.
Sure you can find other ways to do this specific thing, but this is an
example where what the "open" function thought was an error, was
precisely the opposite to the program making the call.
Moving to the context of openscad: Suppose I have a module to make a
screw that takes the "head" as an argument. You could make such a
module to take a number to define what head to use out of a list of
possible heads, but lets say I've structured this differently and I
want to pass the object. (Not sure how to do that in openscad, but
lets assume I can.... Oh! I can do it with a "children" construct!)
Now what if instead of a screw I want to make a threaded rod? No head!
In this case, the module might try to access the empty list of "heads
to put on", but that's not an error. If you report the error, HARD, as
in throw an error and abort, then I can't even create that threaded
rod.
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But
when you think about it, it is not as clear cut as you might think.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110
**
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Yes making warmings optional doesn't really solve the backward
compatibility option. A library maintainer would want to turn it on to make
the library easy to use but would then have to fix all the intended uses of
functions returning undef by adding extra code.
Perhaps it should only be an error to use undef in operations that need a
concrete value, i.e. nearly everywhere, except in a boolean context, where
it is simply false. If the undef value carries information of what
generated it that could be part of the warning when it was used.
So something like this.
list = [];
x = list[0]; // x = undef(list index out of bounds)
if(x)
sphere(x); // Not executed
else
cube(x); // Error cube size parameter is undefined (list index out of
bounds at line 2)
On Wed, 29 Jul 2020 at 08:11, Rogier Wolff <R.E.Wolff@bitwizard.nl> wrote:
> On Tue, Jul 28, 2020 at 08:04:54PM +0000, Jordan Brown wrote:
> 's detected, the harder it will be to find the bug.
> >
> > Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
>
> Some "failures" are not failures, and need to be handled by higher
> layers. The classical example is of a program going to write a file.
> To prevent accidentally overwriting the file, the program will attempt
> to open the file, get an error: "Can't open file", and then proceed to
> write the file.
>
> Sure you can find other ways to do this specific thing, but this is an
> example where what the "open" function thought was an error, was
> precisely the opposite to the program making the call.
>
> Moving to the context of openscad: Suppose I have a module to make a
> screw that takes the "head" as an argument. You could make such a
> module to take a number to define what head to use out of a list of
> possible heads, but lets say I've structured this differently and I
> want to pass the object. (Not sure how to do that in openscad, but
> lets assume I can.... Oh! I can do it with a "children" construct!)
>
> Now what if instead of a screw I want to make a threaded rod? No head!
> In this case, the module might try to access the empty list of "heads
> to put on", but that's not an error. If you report the error, HARD, as
> in throw an error and abort, then I can't even create that threaded
> rod.
>
> So... when talking abstractly: "You should throw errors as close as
> possible to the line causing the problem." I'm all for it.... But
> when you think about it, it is not as clear cut as you might think.
>
>
> Roger.
>
> --
> ** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110
> **
> ** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 **
> The plan was simple, like my brother-in-law Phil. But unlike
> Phil, this plan just might work.
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
NH
nop head
Wed, Jul 29, 2020 8:03 AM
Or better this:
list = [];
x = list[0]; // x = undef(list index out of bounds at line 2)
if(x)
sphere(x); // Not executed
else
cube(x); // Error cube size parameter is undef(list index out of bounds
at line 2) at line 6
On Wed, 29 Jul 2020 at 09:00, nop head nop.head@gmail.com wrote:
Yes making warmings optional doesn't really solve the backward
compatibility option. A library maintainer would want to turn it on to make
the library easy to use but would then have to fix all the intended uses of
functions returning undef by adding extra code.
Perhaps it should only be an error to use undef in operations that need a
concrete value, i.e. nearly everywhere, except in a boolean context, where
it is simply false. If the undef value carries information of what
generated it that could be part of the warning when it was used.
So something like this.
list = [];
x = list[0]; // x = undef(list index out of bounds)
if(x)
sphere(x); // Not executed
else
cube(x); // Error cube size parameter is undefined (list index out of
bounds at line 2)
On Wed, 29 Jul 2020 at 08:11, Rogier Wolff R.E.Wolff@bitwizard.nl wrote:
On Tue, Jul 28, 2020 at 08:04:54PM +0000, Jordan Brown wrote:
's detected, the harder it will be to find the bug.
Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
Some "failures" are not failures, and need to be handled by higher
layers. The classical example is of a program going to write a file.
To prevent accidentally overwriting the file, the program will attempt
to open the file, get an error: "Can't open file", and then proceed to
write the file.
Sure you can find other ways to do this specific thing, but this is an
example where what the "open" function thought was an error, was
precisely the opposite to the program making the call.
Moving to the context of openscad: Suppose I have a module to make a
screw that takes the "head" as an argument. You could make such a
module to take a number to define what head to use out of a list of
possible heads, but lets say I've structured this differently and I
want to pass the object. (Not sure how to do that in openscad, but
lets assume I can.... Oh! I can do it with a "children" construct!)
Now what if instead of a screw I want to make a threaded rod? No head!
In this case, the module might try to access the empty list of "heads
to put on", but that's not an error. If you report the error, HARD, as
in throw an error and abort, then I can't even create that threaded
rod.
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But
when you think about it, it is not as clear cut as you might think.
Roger.
--
** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110
**
** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233
**
The plan was simple, like my brother-in-law Phil. But unlike
Phil, this plan just might work.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Or better this:
list = [];
x = list[0]; // x = undef(list index out of bounds at line 2)
if(x)
sphere(x); // Not executed
else
cube(x); // Error cube size parameter is undef(list index out of bounds
at line 2) at line 6
On Wed, 29 Jul 2020 at 09:00, nop head <nop.head@gmail.com> wrote:
> Yes making warmings optional doesn't really solve the backward
> compatibility option. A library maintainer would want to turn it on to make
> the library easy to use but would then have to fix all the intended uses of
> functions returning undef by adding extra code.
>
> Perhaps it should only be an error to use undef in operations that need a
> concrete value, i.e. nearly everywhere, except in a boolean context, where
> it is simply false. If the undef value carries information of what
> generated it that could be part of the warning when it was used.
>
> So something like this.
> list = [];
> x = list[0]; // x = undef(list index out of bounds)
> if(x)
> sphere(x); // Not executed
> else
> cube(x); // Error cube size parameter is undefined (list index out of
> bounds at line 2)
>
>
>
> On Wed, 29 Jul 2020 at 08:11, Rogier Wolff <R.E.Wolff@bitwizard.nl> wrote:
>
>> On Tue, Jul 28, 2020 at 08:04:54PM +0000, Jordan Brown wrote:
>> 's detected, the harder it will be to find the bug.
>> >
>> > Failing "soft" - returning undef, in OpenSCAD - may feel like a happy
>>
>> Some "failures" are not failures, and need to be handled by higher
>> layers. The classical example is of a program going to write a file.
>> To prevent accidentally overwriting the file, the program will attempt
>> to open the file, get an error: "Can't open file", and then proceed to
>> write the file.
>>
>> Sure you can find other ways to do this specific thing, but this is an
>> example where what the "open" function thought was an error, was
>> precisely the opposite to the program making the call.
>>
>> Moving to the context of openscad: Suppose I have a module to make a
>> screw that takes the "head" as an argument. You could make such a
>> module to take a number to define what head to use out of a list of
>> possible heads, but lets say I've structured this differently and I
>> want to pass the object. (Not sure how to do that in openscad, but
>> lets assume I can.... Oh! I can do it with a "children" construct!)
>>
>> Now what if instead of a screw I want to make a threaded rod? No head!
>> In this case, the module might try to access the empty list of "heads
>> to put on", but that's not an error. If you report the error, HARD, as
>> in throw an error and abort, then I can't even create that threaded
>> rod.
>>
>> So... when talking abstractly: "You should throw errors as close as
>> possible to the line causing the problem." I'm all for it.... But
>> when you think about it, it is not as clear cut as you might think.
>>
>>
>> Roger.
>>
>> --
>> ** R.E.Wolff@BitWizard.nl ** https://www.BitWizard.nl/ ** +31-15-2049110
>> **
>> ** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233
>> **
>> The plan was simple, like my brother-in-law Phil. But unlike
>> Phil, this plan just might work.
>>
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
>
A
adrianv
Wed, Jul 29, 2020 12:40 PM
Some "failures" are not failures, and need to be handled by higher
layers. The classical example is of a program going to write a file.
To prevent accidentally overwriting the file, the program will attempt
to open the file, get an error: "Can't open file", and then proceed to
write the file.
Sure you can find other ways to do this specific thing, but this is an
example where what the "open" function thought was an error, was
precisely the opposite to the program making the call.
....
Now what if instead of a screw I want to make a threaded rod? No head!
In this case, the module might try to access the empty list of "heads
to put on", but that's not an error. If you report the error, HARD, as
in throw an error and abort, then I can't even create that threaded
rod.
...
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But
when you think about it, it is not as clear cut as you might think.
I don't think you can find other ways to test for file existence that aren't
equivalent to trying to open the file. You can divide errors into two
kinds: preventable and not preventable. You have no way of knowing that the
file open operation will fail except that you try it. In OpenSCAD you don't
know that two lines are parallel until the line intersection routine runs,
so it wouldn't make sense to write a line intersection routine that issued a
fatal error on parallel lines, because the line intersection function also
needs to serve as the test for parallel lines.
But if you calculate list[ind] you can guard against errors by not passing
it an invalid index, which is a simple matter since the valid indices are
integers with ind>=0 and ind<len(list). If you calculate
a+b
in some context you can and should validate that a and b are legitimate
values before doing the calculation, which will guarantee validity of a+b.
It's an unlikely context where the validity of a+b is sufficient alone.
Some further testing is necessary to assure that the computation makes sense
in the larger context, which will ensure that a and b are compatible for
addition.
For the example of threaded rod and lists of heads, you check the length of
the list before indexing into it. Your example is a little weird since
screws don't have multiple heads, but if someone passes you a list to
operate on your code is going to look like:
for(entry=list){...} or for(i=[0:1:len(list)-1]){...}
and if the list had no entries, nothing would happen. If you're passing
geometry you check $children before invoking a given child.
I think it's pretty clear cut. If the error is preventable then it is
reasonable to throw the error as soon as it occurs. If the programmer
doesn't want an error then a test can be used to prevent the error. Only in
the case where the error is not preventable does it make sense to do
something different. Indexing out of bounds is preventable. Are there any
OpenSCAD operations that generate undef where performing the operation is
the only way to validate it? If there are they probably should return undef
rather than producing an immediate error. But then if you try to do
operations on that undef you should get an immediate error.
--
Sent from: http://forum.openscad.org/
rew wrote
> Some "failures" are not failures, and need to be handled by higher
> layers. The classical example is of a program going to write a file.
> To prevent accidentally overwriting the file, the program will attempt
> to open the file, get an error: "Can't open file", and then proceed to
> write the file.
>
> Sure you can find other ways to do this specific thing, but this is an
> example where what the "open" function thought was an error, was
> precisely the opposite to the program making the call.
>
> ....
>
> Now what if instead of a screw I want to make a threaded rod? No head!
> In this case, the module might try to access the empty list of "heads
> to put on", but that's not an error. If you report the error, HARD, as
> in throw an error and abort, then I can't even create that threaded
> rod.
>
> ...
>
> So... when talking abstractly: "You should throw errors as close as
> possible to the line causing the problem." I'm all for it.... But
> when you think about it, it is not as clear cut as you might think.
I don't think you can find other ways to test for file existence that aren't
equivalent to trying to open the file. You can divide errors into two
kinds: preventable and not preventable. You have no way of knowing that the
file open operation will fail except that you try it. In OpenSCAD you don't
know that two lines are parallel until the line intersection routine runs,
so it wouldn't make sense to write a line intersection routine that issued a
fatal error on parallel lines, because the line intersection function also
needs to serve as the test for parallel lines.
But if you calculate list[ind] you can guard against errors by not passing
it an invalid index, which is a simple matter since the valid indices are
integers with ind>=0 and ind<len(list). If you calculate
a+b
in some context you can and should validate that a and b are legitimate
values before doing the calculation, which will guarantee validity of a+b.
It's an unlikely context where the validity of a+b is sufficient alone.
Some further testing is necessary to assure that the computation makes sense
in the larger context, which will ensure that a and b are compatible for
addition.
For the example of threaded rod and lists of heads, you check the length of
the list before indexing into it. Your example is a little weird since
screws don't have multiple heads, but if someone passes you a list to
operate on your code is going to look like:
for(entry=list){...} or for(i=[0:1:len(list)-1]){...}
and if the list had no entries, nothing would happen. If you're passing
geometry you check $children before invoking a given child.
I think it's pretty clear cut. If the error is preventable then it is
reasonable to throw the error as soon as it occurs. If the programmer
doesn't want an error then a test can be used to prevent the error. Only in
the case where the error is not preventable does it make sense to do
something different. Indexing out of bounds is preventable. Are there any
OpenSCAD operations that generate undef where performing the operation is
the only way to validate it? If there are they probably should return undef
rather than producing an immediate error. But then if you try to do
operations on that undef you should get an immediate error.
--
Sent from: http://forum.openscad.org/
JB
Jordan Brown
Wed, Jul 29, 2020 6:08 PM
[ Please forgive my tendency to write a lot about this. It's actually a
primary interest in my professional life - how to write programs to make
it difficult to have bugs, how to detect the bugs you can't prevent, and
how to design languages, libraries, and other infrastructure so as to
prevent and detect bugs. A related topic is "how to report errors so
that the error reports are useful". ]
On 7/29/2020 12:10 AM, Rogier Wolff wrote:
Some "failures" are not failures, [...]
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But when
you think about it, it is not as clear cut as you might think.
Yes. Distinguishing a likely bug from an "expected" error is important.
Nop head's "when you need a concrete answer" test is certainly a good
start. But I don't think it's quite enough. The next question is "what
is a likely bug, and what is merely missing data?".
A missing parameter (in OpenSCAD, where missing parameters are expected)
is clearly not an error. (But maybe OpenSCAD should have a syntax that
says "this parameter is required".)
An attempt to divide by zero is clearly an error. In the unlikely event
that you really want something infinity-like as the result, require the
programmer to explicitly check first. (atan2() comes to mind.)
I think it's clear that if a function requires a particular parameter,
then not supplying it is an error. (But not everybody agrees. In C, I
often see people checking required parameters for NULL and returning an
error if they're NULL. No! The parameter is required; if the caller
supplied a NULL then the caller unquestionably has a bug!)
But an array index that's out of bounds? Hard to say. You might say
it's a bug, or you might say that returning undef allows the program to
probe for the dimensions of the array. I would come down on the side of
calling it an error. If you want to probe for the dimensions of the
array, you can use len() and then the intent is obvious. That's a key
thing: having the program convey the programmer's intent. If you have
out-of-bounds references simply return undef, neither the interpreter
nor a reviewer can tell what the intent is, and so you can't detect the
bug. If, on the other hand, you consider it an error, then you have two
distinct idioms:Â if your intent is to retrieve a value (and you don't
expect your index to be out of bounds) then you do a simple reference;
if your intent is to check for the presence of the value then you use
len(). Yes, if "undef" was a reasonable default value you have to go a
bit out of your way and say i < len(x) ? x[i] : undef, but that doesn't
seem like an onerous price to pay for protecting all of the other cases.
Mixed-type expressions are particularly tricky. There are reasonable
definitions for many combinations. I'd draw the line at the point where
you have to "stretch" to say that the definition is reasonable. String
concatenation through '+' is reasonable. String replication through
multiplication seems reasonable. Saying that boolean*string is
equivalent to (boolean ? string : "") seems past the limit. Automatic
to-string when you mix a string and something else with "+"... I'm not
sure. It's reasonably obvious and awfully convenient, but is implicated
in any number of cases of "null" appearing in user interfaces.
Hans's "undef with a reason" does help a lot. I'm not sure it's
enough. Because undef is considered false for "if" purposes (a
definition that seems unlikely to change) an undef that came from a bug
can lead to silently going down an execution path that doesn't happen to
ever use the value in a "concrete" way.
[ Please forgive my tendency to write a lot about this. It's actually a
primary interest in my professional life - how to write programs to make
it difficult to have bugs, how to detect the bugs you can't prevent, and
how to design languages, libraries, and other infrastructure so as to
prevent and detect bugs. A related topic is "how to report errors so
that the error reports are useful". ]
On 7/29/2020 12:10 AM, Rogier Wolff wrote:
> Some "failures" are not failures, [...]
>
> So... when talking abstractly: "You should throw errors as close as
> possible to the line causing the problem." I'm all for it.... But when
> you think about it, it is not as clear cut as you might think.
>
Yes. Distinguishing a likely bug from an "expected" error is important.
Nop head's "when you need a concrete answer" test is certainly a good
start. But I don't think it's quite enough. The next question is "what
is a likely bug, and what is merely missing data?".
A missing parameter (in OpenSCAD, where missing parameters are expected)
is clearly not an error. (But maybe OpenSCAD should have a syntax that
says "this parameter is required".)
An attempt to divide by zero is clearly an error. In the unlikely event
that you really want something infinity-like as the result, require the
programmer to explicitly check first. (atan2() comes to mind.)
I think it's clear that if a function requires a particular parameter,
then not supplying it is an error. (But not everybody agrees. In C, I
often see people checking required parameters for NULL and returning an
error if they're NULL. No! The parameter is required; if the caller
supplied a NULL then the caller unquestionably has a bug!)
But an array index that's out of bounds? Hard to say. You might say
it's a bug, or you might say that returning undef allows the program to
probe for the dimensions of the array. I would come down on the side of
calling it an error. If you *want* to probe for the dimensions of the
array, you can use len() and then the intent is obvious. That's a key
thing: having the program convey the programmer's intent. If you have
out-of-bounds references simply return undef, neither the interpreter
nor a reviewer can tell what the intent is, and so you can't detect the
bug. If, on the other hand, you consider it an error, then you have two
distinct idioms:Â if your intent is to retrieve a value (and you don't
expect your index to be out of bounds) then you do a simple reference;
if your intent is to check for the presence of the value then you use
len(). Yes, if "undef" was a reasonable default value you have to go a
bit out of your way and say i < len(x) ? x[i] : undef, but that doesn't
seem like an onerous price to pay for protecting all of the *other* cases.
Mixed-type expressions are particularly tricky. There are reasonable
definitions for many combinations. I'd draw the line at the point where
you have to "stretch" to say that the definition is reasonable. String
concatenation through '+' is reasonable. String replication through
multiplication seems reasonable. Saying that boolean*string is
equivalent to (boolean ? string : "") seems past the limit. Automatic
to-string when you mix a string and something else with "+"... I'm not
sure. It's reasonably obvious and awfully convenient, but is implicated
in any number of cases of "null" appearing in user interfaces.
Hans's "undef with a reason" does help a lot. I'm not sure it's
enough. Because undef is considered false for "if" purposes (a
definition that seems unlikely to change) an undef that came from a bug
can lead to silently going down an execution path that doesn't happen to
ever use the value in a "concrete" way.
NH
nop head
Wed, Jul 29, 2020 6:56 PM
The issue is that OpenSCAD has been used for many years and has always
returned undef for an out of bounds index so I use it as a shortcut.
Instead of if(len(type) > 3 && type[3]) is just if(type[3]), which is a lot
less ugly. It saves adding empty values to the end of my object descriptors
and makes the lack of an entry the same as an empty entry, which makes
semantic sense to me. And len should go back to silently returning undef
when applied to a scalar. Otherwise it becomes if(is_list(type) &&
len(type) > 3 && type[3]), which is even more ugly. In practice the list
element is wrapped in an accessor function, so the intent becomes obvious.
E.g.
conn = camera_connector_size(type);
if(conn) {
use conn to draw the connector
}
I doubt there are many cases where people do if(list[n]) where it is a
mistake, using list[n] to compute something or compare it to anything
other than undef is definitely an error if the list isn't that long and my
proposal would catch all other errors due to bad indexing.
If an undef value goes down an execution path that doesn't use it then it
isn't a bug as far as I am concerned. undef exists as a value, can be
assigned to variables and passed as parameters and silently tested as false
with if, so I don't think that should change. But if it always stores how
it became undef then debugging is easy because if you ever use it in an
expression you will get a warning showing where it became undef and where
it was finally used. Exactly what you need to debug.
Whether operators get overloaded to work on strings in the future doesn't
matter. At the moment they don't so you get undef.
On Wed, 29 Jul 2020 at 19:09, Jordan Brown openscad@jordan.maileater.net
wrote:
[ Please forgive my tendency to write a lot about this. It's actually a
primary interest in my professional life - how to write programs to make it
difficult to have bugs, how to detect the bugs you can't prevent, and how
to design languages, libraries, and other infrastructure so as to prevent
and detect bugs. A related topic is "how to report errors so that the error
reports are useful". ]
On 7/29/2020 12:10 AM, Rogier Wolff wrote:
Some "failures" are not failures, [...]
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But when you
think about it, it is not as clear cut as you might think.
Yes. Distinguishing a likely bug from an "expected" error is important.
Nop head's "when you need a concrete answer" test is certainly a good
start. But I don't think it's quite enough. The next question is "what is
a likely bug, and what is merely missing data?".
A missing parameter (in OpenSCAD, where missing parameters are expected)
is clearly not an error. (But maybe OpenSCAD should have a syntax that
says "this parameter is required".)
An attempt to divide by zero is clearly an error. In the unlikely event
that you really want something infinity-like as the result, require the
programmer to explicitly check first. (atan2() comes to mind.)
I think it's clear that if a function requires a particular parameter,
then not supplying it is an error. (But not everybody agrees. In C, I
often see people checking required parameters for NULL and returning an
error if they're NULL. No! The parameter is required; if the caller
supplied a NULL then the caller unquestionably has a bug!)
But an array index that's out of bounds? Hard to say. You might say it's
a bug, or you might say that returning undef allows the program to probe
for the dimensions of the array. I would come down on the side of calling
it an error. If you want to probe for the dimensions of the array, you
can use len() and then the intent is obvious. That's a key thing:
having the program convey the programmer's intent. If you have
out-of-bounds references simply return undef, neither the interpreter nor a
reviewer can tell what the intent is, and so you can't detect the bug. If,
on the other hand, you consider it an error, then you have two distinct
idioms: if your intent is to retrieve a value (and you don't expect your
index to be out of bounds) then you do a simple reference; if your intent
is to check for the presence of the value then you use len(). Yes, if
"undef" was a reasonable default value you have to go a bit out of your way
and say i < len(x) ? x[i] : undef, but that doesn't seem like an onerous
price to pay for protecting all of the other cases.
Mixed-type expressions are particularly tricky. There are reasonable
definitions for many combinations. I'd draw the line at the point where
you have to "stretch" to say that the definition is reasonable. String
concatenation through '+' is reasonable. String replication through
multiplication seems reasonable. Saying that boolean*string is equivalent
to (boolean ? string : "") seems past the limit. Automatic to-string
when you mix a string and something else with "+"... I'm not sure. It's
reasonably obvious and awfully convenient, but is implicated in any number
of cases of "null" appearing in user interfaces.
Hans's "undef with a reason" does help a lot. I'm not sure it's enough.
Because undef is considered false for "if" purposes (a definition that
seems unlikely to change) an undef that came from a bug can lead to
silently going down an execution path that doesn't happen to ever use the
value in a "concrete" way.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
The issue is that OpenSCAD has been used for many years and has always
returned undef for an out of bounds index so I use it as a shortcut.
Instead of if(len(type) > 3 && type[3]) is just if(type[3]), which is a lot
less ugly. It saves adding empty values to the end of my object descriptors
and makes the lack of an entry the same as an empty entry, which makes
semantic sense to me. And len should go back to silently returning undef
when applied to a scalar. Otherwise it becomes if(is_list(type) &&
len(type) > 3 && type[3]), which is even more ugly. In practice the list
element is wrapped in an accessor function, so the intent becomes obvious.
E.g.
conn = camera_connector_size(type);
if(conn) {
use conn to draw the connector
}
I doubt there are many cases where people do if(list[n]) where it is a
mistake, using list[n] to compute something or compare it to anything
other than undef is definitely an error if the list isn't that long and my
proposal would catch all other errors due to bad indexing.
If an undef value goes down an execution path that doesn't use it then it
isn't a bug as far as I am concerned. undef exists as a value, can be
assigned to variables and passed as parameters and silently tested as false
with if, so I don't think that should change. But if it always stores how
it became undef then debugging is easy because if you ever use it in an
expression you will get a warning showing where it became undef and where
it was finally used. Exactly what you need to debug.
Whether operators get overloaded to work on strings in the future doesn't
matter. At the moment they don't so you get undef.
On Wed, 29 Jul 2020 at 19:09, Jordan Brown <openscad@jordan.maileater.net>
wrote:
> [ Please forgive my tendency to write a lot about this. It's actually a
> primary interest in my professional life - how to write programs to make it
> difficult to have bugs, how to detect the bugs you can't prevent, and how
> to design languages, libraries, and other infrastructure so as to prevent
> and detect bugs. A related topic is "how to report errors so that the error
> reports are useful". ]
>
> On 7/29/2020 12:10 AM, Rogier Wolff wrote:
>
> Some "failures" are not failures, [...]
>
> So... when talking abstractly: "You should throw errors as close as
> possible to the line causing the problem." I'm all for it.... But when you
> think about it, it is not as clear cut as you might think.
>
>
> Yes. Distinguishing a likely bug from an "expected" error is important.
>
> Nop head's "when you need a concrete answer" test is certainly a good
> start. But I don't think it's quite enough. The next question is "what is
> a likely bug, and what is merely missing data?".
>
> A missing parameter (in OpenSCAD, where missing parameters are expected)
> is clearly not an error. (But maybe OpenSCAD should have a syntax that
> says "this parameter is required".)
>
> An attempt to divide by zero is clearly an error. In the unlikely event
> that you really want something infinity-like as the result, require the
> programmer to explicitly check first. (atan2() comes to mind.)
>
> I think it's clear that if a function requires a particular parameter,
> then not supplying it is an error. (But not everybody agrees. In C, I
> often see people checking required parameters for NULL and returning an
> error if they're NULL. No! The parameter is required; if the caller
> supplied a NULL then the caller unquestionably has a bug!)
>
> But an array index that's out of bounds? Hard to say. You might say it's
> a bug, or you might say that returning undef allows the program to probe
> for the dimensions of the array. I would come down on the side of calling
> it an error. If you *want* to probe for the dimensions of the array, you
> can use len() and then the intent is obvious. That's a key thing:
> having the program convey the programmer's intent. If you have
> out-of-bounds references simply return undef, neither the interpreter nor a
> reviewer can tell what the intent is, and so you can't detect the bug. If,
> on the other hand, you consider it an error, then you have two distinct
> idioms: if your intent is to retrieve a value (and you don't expect your
> index to be out of bounds) then you do a simple reference; if your intent
> is to check for the presence of the value then you use len(). Yes, if
> "undef" was a reasonable default value you have to go a bit out of your way
> and say i < len(x) ? x[i] : undef, but that doesn't seem like an onerous
> price to pay for protecting all of the *other* cases.
>
> Mixed-type expressions are particularly tricky. There are reasonable
> definitions for many combinations. I'd draw the line at the point where
> you have to "stretch" to say that the definition is reasonable. String
> concatenation through '+' is reasonable. String replication through
> multiplication seems reasonable. Saying that boolean*string is equivalent
> to (boolean ? string : "") seems past the limit. Automatic to-string
> when you mix a string and something else with "+"... I'm not sure. It's
> reasonably obvious and awfully convenient, but is implicated in any number
> of cases of "null" appearing in user interfaces.
>
> Hans's "undef with a reason" does help a lot. I'm not sure it's enough.
> Because undef is considered false for "if" purposes (a definition that
> seems unlikely to change) an undef that came from a bug can lead to
> silently going down an execution path that doesn't happen to ever use the
> value in a "concrete" way.
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Wed, Jul 29, 2020 8:54 PM
There's no reason this is true. What if I have a parameter, x, and I want to
based a decision on the value of x, but in some complex way, so I write
f(x) ? g(x) : h(x)
but unfortunately, the function f has a bug and sometimes produces undef
when it should produce true. My code now silently executes h(x) when it
should instead run g(x) and there's no sign of the mistake. Maybe this
later leads to a failure of some kind? But it's not direct. The idea that
bugs can only occur if the expression is used outside a boolean context is
incorrect.
A simpler and less complex case would be a decision parameter mistakenly
ends up somehow as undef and hence gets interpreted as false. The program
doesn't produce bad output, but it produces the wrong output, which is a
bug. This might happen if the programmer mistyped a variable name, for
example.
if (add_dooodad) { make_doodad(..)}
This is always false since add_dooodad was spelled wrong so it is undef, and
so the doodad is never created, and the programmer is absolutely baffled
because the code looks correct, and add_doodad is definitely set to true.
nophead wrote
If an undef value goes down an execution path that doesn't use it then it
isn't a bug as far as I am concerned. undef exists as a value, can be
assigned to variables and passed as parameters and silently tested as
false
with if, so I don't think that should change. But if it always stores how
it became undef then debugging is easy because if you ever use it in an
expression you will get a warning showing where it became undef and where
it was finally used. Exactly what you need to debug.
Whether operators get overloaded to work on strings in the future doesn't
matter. At the moment they don't so you get undef.
On Wed, 29 Jul 2020 at 19:09, Jordan Brown <
[ Please forgive my tendency to write a lot about this. It's actually a
primary interest in my professional life - how to write programs to make
it
difficult to have bugs, how to detect the bugs you can't prevent, and how
to design languages, libraries, and other infrastructure so as to prevent
and detect bugs. A related topic is "how to report errors so that the
error
reports are useful". ]
On 7/29/2020 12:10 AM, Rogier Wolff wrote:
Some "failures" are not failures, [...]
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But when
you
think about it, it is not as clear cut as you might think.
Yes. Distinguishing a likely bug from an "expected" error is important.
Nop head's "when you need a concrete answer" test is certainly a good
start. But I don't think it's quite enough. The next question is "what
is
a likely bug, and what is merely missing data?".
A missing parameter (in OpenSCAD, where missing parameters are expected)
is clearly not an error. (But maybe OpenSCAD should have a syntax that
says "this parameter is required".)
An attempt to divide by zero is clearly an error. In the unlikely event
that you really want something infinity-like as the result, require the
programmer to explicitly check first. (atan2() comes to mind.)
I think it's clear that if a function requires a particular parameter,
then not supplying it is an error. (But not everybody agrees. In C, I
often see people checking required parameters for NULL and returning an
error if they're NULL. No! The parameter is required; if the caller
supplied a NULL then the caller unquestionably has a bug!)
But an array index that's out of bounds? Hard to say. You might say
it's
a bug, or you might say that returning undef allows the program to probe
for the dimensions of the array. I would come down on the side of
calling
it an error. If you want to probe for the dimensions of the array, you
can use len() and then the intent is obvious. That's a key thing:
having the program convey the programmer's intent. If you have
out-of-bounds references simply return undef, neither the interpreter nor
a
reviewer can tell what the intent is, and so you can't detect the bug.
If,
on the other hand, you consider it an error, then you have two distinct
idioms: if your intent is to retrieve a value (and you don't expect your
index to be out of bounds) then you do a simple reference; if your intent
is to check for the presence of the value then you use len(). Yes, if
"undef" was a reasonable default value you have to go a bit out of your
way
and say i < len(x) ? x[i] : undef, but that doesn't seem like an onerous
price to pay for protecting all of the other cases.
Mixed-type expressions are particularly tricky. There are reasonable
definitions for many combinations. I'd draw the line at the point where
you have to "stretch" to say that the definition is reasonable. String
concatenation through '+' is reasonable. String replication through
multiplication seems reasonable. Saying that boolean*string is
equivalent
to (boolean ? string : "") seems past the limit. Automatic to-string
when you mix a string and something else with "+"... I'm not sure. It's
reasonably obvious and awfully convenient, but is implicated in any
number
of cases of "null" appearing in user interfaces.
Hans's "undef with a reason" does help a lot. I'm not sure it's enough.
Because undef is considered false for "if" purposes (a definition that
seems unlikely to change) an undef that came from a bug can lead to
silently going down an execution path that doesn't happen to ever use the
value in a "concrete" way.
OpenSCAD mailing list
There's no reason this is true. What if I have a parameter, x, and I want to
based a decision on the value of x, but in some complex way, so I write
f(x) ? g(x) : h(x)
but unfortunately, the function f has a bug and sometimes produces undef
when it should produce true. My code now silently executes h(x) when it
should instead run g(x) and there's no sign of the mistake. Maybe this
later leads to a failure of some kind? But it's not direct. The idea that
bugs can only occur if the expression is used outside a boolean context is
incorrect.
A simpler and less complex case would be a decision parameter mistakenly
ends up somehow as undef and hence gets interpreted as false. The program
doesn't produce bad output, but it produces the wrong output, which is a
bug. This might happen if the programmer mistyped a variable name, for
example.
if (add_dooodad) { make_doodad(..)}
This is always false since add_dooodad was spelled wrong so it is undef, and
so the doodad is never created, and the programmer is absolutely baffled
because the code looks correct, and add_doodad is definitely set to true.
nophead wrote
> If an undef value goes down an execution path that doesn't use it then it
> isn't a bug as far as I am concerned. undef exists as a value, can be
> assigned to variables and passed as parameters and silently tested as
> false
> with if, so I don't think that should change. But if it always stores how
> it became undef then debugging is easy because if you ever use it in an
> expression you will get a warning showing where it became undef and where
> it was finally used. Exactly what you need to debug.
>
> Whether operators get overloaded to work on strings in the future doesn't
> matter. At the moment they don't so you get undef.
>
>
> On Wed, 29 Jul 2020 at 19:09, Jordan Brown <
> openscad@.maileater
> >
> wrote:
>
>> [ Please forgive my tendency to write a lot about this. It's actually a
>> primary interest in my professional life - how to write programs to make
>> it
>> difficult to have bugs, how to detect the bugs you can't prevent, and how
>> to design languages, libraries, and other infrastructure so as to prevent
>> and detect bugs. A related topic is "how to report errors so that the
>> error
>> reports are useful". ]
>>
>> On 7/29/2020 12:10 AM, Rogier Wolff wrote:
>>
>> Some "failures" are not failures, [...]
>>
>> So... when talking abstractly: "You should throw errors as close as
>> possible to the line causing the problem." I'm all for it.... But when
>> you
>> think about it, it is not as clear cut as you might think.
>>
>>
>> Yes. Distinguishing a likely bug from an "expected" error is important.
>>
>> Nop head's "when you need a concrete answer" test is certainly a good
>> start. But I don't think it's quite enough. The next question is "what
>> is
>> a likely bug, and what is merely missing data?".
>>
>> A missing parameter (in OpenSCAD, where missing parameters are expected)
>> is clearly not an error. (But maybe OpenSCAD should have a syntax that
>> says "this parameter is required".)
>>
>> An attempt to divide by zero is clearly an error. In the unlikely event
>> that you really want something infinity-like as the result, require the
>> programmer to explicitly check first. (atan2() comes to mind.)
>>
>> I think it's clear that if a function requires a particular parameter,
>> then not supplying it is an error. (But not everybody agrees. In C, I
>> often see people checking required parameters for NULL and returning an
>> error if they're NULL. No! The parameter is required; if the caller
>> supplied a NULL then the caller unquestionably has a bug!)
>>
>> But an array index that's out of bounds? Hard to say. You might say
>> it's
>> a bug, or you might say that returning undef allows the program to probe
>> for the dimensions of the array. I would come down on the side of
>> calling
>> it an error. If you *want* to probe for the dimensions of the array, you
>> can use len() and then the intent is obvious. That's a key thing:
>> having the program convey the programmer's intent. If you have
>> out-of-bounds references simply return undef, neither the interpreter nor
>> a
>> reviewer can tell what the intent is, and so you can't detect the bug.
>> If,
>> on the other hand, you consider it an error, then you have two distinct
>> idioms: if your intent is to retrieve a value (and you don't expect your
>> index to be out of bounds) then you do a simple reference; if your intent
>> is to check for the presence of the value then you use len(). Yes, if
>> "undef" was a reasonable default value you have to go a bit out of your
>> way
>> and say i < len(x) ? x[i] : undef, but that doesn't seem like an onerous
>> price to pay for protecting all of the *other* cases.
>>
>> Mixed-type expressions are particularly tricky. There are reasonable
>> definitions for many combinations. I'd draw the line at the point where
>> you have to "stretch" to say that the definition is reasonable. String
>> concatenation through '+' is reasonable. String replication through
>> multiplication seems reasonable. Saying that boolean*string is
>> equivalent
>> to (boolean ? string : "") seems past the limit. Automatic to-string
>> when you mix a string and something else with "+"... I'm not sure. It's
>> reasonably obvious and awfully convenient, but is implicated in any
>> number
>> of cases of "null" appearing in user interfaces.
>>
>> Hans's "undef with a reason" does help a lot. I'm not sure it's enough.
>> Because undef is considered false for "if" purposes (a definition that
>> seems unlikely to change) an undef that came from a bug can lead to
>> silently going down an execution path that doesn't happen to ever use the
>> value in a "concrete" way.
>>
>> _______________________________________________
>> 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/
NH
nop head
Wed, Jul 29, 2020 9:10 PM
Well then it is a trade off between people that like to write succinct code
versus those that write bugs.
The point is OpenSCAD has this feature, so in order to maintain backwards
compatibility it should keep it. Does it make sense I have to add more and
more code to make OpenSCAD do what it used to do without complaining?
if (add_dooodad) { make_doodad(..)} already creates a warning because you
have used a variable that does not exist, different from using an
expression that yields undef.
On Wed, 29 Jul 2020 at 21:55, adrianv avm4@cornell.edu wrote:
There's no reason this is true. What if I have a parameter, x, and I want
to
based a decision on the value of x, but in some complex way, so I write
f(x) ? g(x) : h(x)
but unfortunately, the function f has a bug and sometimes produces undef
when it should produce true. My code now silently executes h(x) when it
should instead run g(x) and there's no sign of the mistake. Maybe this
later leads to a failure of some kind? But it's not direct. The idea that
bugs can only occur if the expression is used outside a boolean context is
incorrect.
A simpler and less complex case would be a decision parameter mistakenly
ends up somehow as undef and hence gets interpreted as false. The program
doesn't produce bad output, but it produces the wrong output, which is a
bug. This might happen if the programmer mistyped a variable name, for
example.
if (add_dooodad) { make_doodad(..)}
This is always false since add_dooodad was spelled wrong so it is undef,
and
so the doodad is never created, and the programmer is absolutely baffled
because the code looks correct, and add_doodad is definitely set to true.
nophead wrote
If an undef value goes down an execution path that doesn't use it then it
isn't a bug as far as I am concerned. undef exists as a value, can be
assigned to variables and passed as parameters and silently tested as
false
with if, so I don't think that should change. But if it always stores how
it became undef then debugging is easy because if you ever use it in an
expression you will get a warning showing where it became undef and where
it was finally used. Exactly what you need to debug.
Whether operators get overloaded to work on strings in the future doesn't
matter. At the moment they don't so you get undef.
On Wed, 29 Jul 2020 at 19:09, Jordan Brown <
[ Please forgive my tendency to write a lot about this. It's actually a
primary interest in my professional life - how to write programs to make
it
difficult to have bugs, how to detect the bugs you can't prevent, and
to design languages, libraries, and other infrastructure so as to
and detect bugs. A related topic is "how to report errors so that the
error
reports are useful". ]
On 7/29/2020 12:10 AM, Rogier Wolff wrote:
Some "failures" are not failures, [...]
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But when
you
think about it, it is not as clear cut as you might think.
Yes. Distinguishing a likely bug from an "expected" error is important.
Nop head's "when you need a concrete answer" test is certainly a good
start. But I don't think it's quite enough. The next question is "what
is
a likely bug, and what is merely missing data?".
A missing parameter (in OpenSCAD, where missing parameters are expected)
is clearly not an error. (But maybe OpenSCAD should have a syntax that
says "this parameter is required".)
An attempt to divide by zero is clearly an error. In the unlikely event
that you really want something infinity-like as the result, require the
programmer to explicitly check first. (atan2() comes to mind.)
I think it's clear that if a function requires a particular parameter,
then not supplying it is an error. (But not everybody agrees. In C, I
often see people checking required parameters for NULL and returning an
error if they're NULL. No! The parameter is required; if the caller
supplied a NULL then the caller unquestionably has a bug!)
But an array index that's out of bounds? Hard to say. You might say
it's
a bug, or you might say that returning undef allows the program to probe
for the dimensions of the array. I would come down on the side of
calling
it an error. If you want to probe for the dimensions of the array,
can use len() and then the intent is obvious. That's a key thing:
having the program convey the programmer's intent. If you have
out-of-bounds references simply return undef, neither the interpreter
a
reviewer can tell what the intent is, and so you can't detect the bug.
If,
on the other hand, you consider it an error, then you have two distinct
idioms: if your intent is to retrieve a value (and you don't expect
index to be out of bounds) then you do a simple reference; if your
is to check for the presence of the value then you use len(). Yes, if
"undef" was a reasonable default value you have to go a bit out of your
way
and say i < len(x) ? x[i] : undef, but that doesn't seem like an onerous
price to pay for protecting all of the other cases.
Mixed-type expressions are particularly tricky. There are reasonable
definitions for many combinations. I'd draw the line at the point where
you have to "stretch" to say that the definition is reasonable. String
concatenation through '+' is reasonable. String replication through
multiplication seems reasonable. Saying that boolean*string is
equivalent
to (boolean ? string : "") seems past the limit. Automatic to-string
when you mix a string and something else with "+"... I'm not sure. It's
reasonably obvious and awfully convenient, but is implicated in any
number
of cases of "null" appearing in user interfaces.
Hans's "undef with a reason" does help a lot. I'm not sure it's enough.
Because undef is considered false for "if" purposes (a definition that
seems unlikely to change) an undef that came from a bug can lead to
silently going down an execution path that doesn't happen to ever use
value in a "concrete" way.
OpenSCAD mailing list
Well then it is a trade off between people that like to write succinct code
versus those that write bugs.
The point is OpenSCAD has this feature, so in order to maintain backwards
compatibility it should keep it. Does it make sense I have to add more and
more code to make OpenSCAD do what it used to do without complaining?
if (add_dooodad) { make_doodad(..)} already creates a warning because you
have used a variable that does not exist, different from using an
expression that yields undef.
On Wed, 29 Jul 2020 at 21:55, adrianv <avm4@cornell.edu> wrote:
> There's no reason this is true. What if I have a parameter, x, and I want
> to
> based a decision on the value of x, but in some complex way, so I write
>
> f(x) ? g(x) : h(x)
>
> but unfortunately, the function f has a bug and sometimes produces undef
> when it should produce true. My code now silently executes h(x) when it
> should instead run g(x) and there's no sign of the mistake. Maybe this
> later leads to a failure of some kind? But it's not direct. The idea that
> bugs can only occur if the expression is used outside a boolean context is
> incorrect.
>
> A simpler and less complex case would be a decision parameter mistakenly
> ends up somehow as undef and hence gets interpreted as false. The program
> doesn't produce bad output, but it produces the wrong output, which is a
> bug. This might happen if the programmer mistyped a variable name, for
> example.
>
> if (add_dooodad) { make_doodad(..)}
>
> This is always false since add_dooodad was spelled wrong so it is undef,
> and
> so the doodad is never created, and the programmer is absolutely baffled
> because the code looks correct, and add_doodad is definitely set to true.
>
>
> nophead wrote
> > If an undef value goes down an execution path that doesn't use it then it
> > isn't a bug as far as I am concerned. undef exists as a value, can be
> > assigned to variables and passed as parameters and silently tested as
> > false
> > with if, so I don't think that should change. But if it always stores how
> > it became undef then debugging is easy because if you ever use it in an
> > expression you will get a warning showing where it became undef and where
> > it was finally used. Exactly what you need to debug.
> >
> > Whether operators get overloaded to work on strings in the future doesn't
> > matter. At the moment they don't so you get undef.
> >
> >
> > On Wed, 29 Jul 2020 at 19:09, Jordan Brown <
>
> > openscad@.maileater
>
> > >
> > wrote:
> >
> >> [ Please forgive my tendency to write a lot about this. It's actually a
> >> primary interest in my professional life - how to write programs to make
> >> it
> >> difficult to have bugs, how to detect the bugs you can't prevent, and
> how
> >> to design languages, libraries, and other infrastructure so as to
> prevent
> >> and detect bugs. A related topic is "how to report errors so that the
> >> error
> >> reports are useful". ]
> >>
> >> On 7/29/2020 12:10 AM, Rogier Wolff wrote:
> >>
> >> Some "failures" are not failures, [...]
> >>
> >> So... when talking abstractly: "You should throw errors as close as
> >> possible to the line causing the problem." I'm all for it.... But when
> >> you
> >> think about it, it is not as clear cut as you might think.
> >>
> >>
> >> Yes. Distinguishing a likely bug from an "expected" error is important.
> >>
> >> Nop head's "when you need a concrete answer" test is certainly a good
> >> start. But I don't think it's quite enough. The next question is "what
> >> is
> >> a likely bug, and what is merely missing data?".
> >>
> >> A missing parameter (in OpenSCAD, where missing parameters are expected)
> >> is clearly not an error. (But maybe OpenSCAD should have a syntax that
> >> says "this parameter is required".)
> >>
> >> An attempt to divide by zero is clearly an error. In the unlikely event
> >> that you really want something infinity-like as the result, require the
> >> programmer to explicitly check first. (atan2() comes to mind.)
> >>
> >> I think it's clear that if a function requires a particular parameter,
> >> then not supplying it is an error. (But not everybody agrees. In C, I
> >> often see people checking required parameters for NULL and returning an
> >> error if they're NULL. No! The parameter is required; if the caller
> >> supplied a NULL then the caller unquestionably has a bug!)
> >>
> >> But an array index that's out of bounds? Hard to say. You might say
> >> it's
> >> a bug, or you might say that returning undef allows the program to probe
> >> for the dimensions of the array. I would come down on the side of
> >> calling
> >> it an error. If you *want* to probe for the dimensions of the array,
> you
> >> can use len() and then the intent is obvious. That's a key thing:
> >> having the program convey the programmer's intent. If you have
> >> out-of-bounds references simply return undef, neither the interpreter
> nor
> >> a
> >> reviewer can tell what the intent is, and so you can't detect the bug.
> >> If,
> >> on the other hand, you consider it an error, then you have two distinct
> >> idioms: if your intent is to retrieve a value (and you don't expect
> your
> >> index to be out of bounds) then you do a simple reference; if your
> intent
> >> is to check for the presence of the value then you use len(). Yes, if
> >> "undef" was a reasonable default value you have to go a bit out of your
> >> way
> >> and say i < len(x) ? x[i] : undef, but that doesn't seem like an onerous
> >> price to pay for protecting all of the *other* cases.
> >>
> >> Mixed-type expressions are particularly tricky. There are reasonable
> >> definitions for many combinations. I'd draw the line at the point where
> >> you have to "stretch" to say that the definition is reasonable. String
> >> concatenation through '+' is reasonable. String replication through
> >> multiplication seems reasonable. Saying that boolean*string is
> >> equivalent
> >> to (boolean ? string : "") seems past the limit. Automatic to-string
> >> when you mix a string and something else with "+"... I'm not sure. It's
> >> reasonably obvious and awfully convenient, but is implicated in any
> >> number
> >> of cases of "null" appearing in user interfaces.
> >>
> >> Hans's "undef with a reason" does help a lot. I'm not sure it's enough.
> >> Because undef is considered false for "if" purposes (a definition that
> >> seems unlikely to change) an undef that came from a bug can lead to
> >> silently going down an execution path that doesn't happen to ever use
> the
> >> value in a "concrete" way.
> >>
> >> _______________________________________________
> >> 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/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
RP
Ronaldo Persiano
Wed, Jul 29, 2020 9:55 PM
Well then it is a trade off between people that like to write
succinct code versus those that write bugs.
Well, I don't like bugs. I prefer to be cautious than lazy. Anyway, isn't
that succinct enough?
function in_bounds(var,i) = is_list(var) && i>=0 && i<len(var);
...
if(in_bounds(arr)) ...
>
> Well then it is a trade off between people that like to write
> succinct code versus those that write bugs.
Well, I don't like bugs. I prefer to be cautious than lazy. Anyway, isn't
that succinct enough?
function in_bounds(var,i) = is_list(var) && i>=0 && i<len(var);
...
if(in_bounds(arr)) ...
NH
nop head
Wed, Jul 29, 2020 10:15 PM
No it's horrible. Instead of
function camera_connector_size(type)= type[6];
It would be
function camera_connector_size(type)= in_bounds(type, 6) ? type[6] : false;
Longer, slower, less readable.
On Wed, 29 Jul 2020 at 22:56, Ronaldo Persiano rcmpersiano@gmail.com
wrote:
Well then it is a trade off between people that like to write
succinct code versus those that write bugs.
No it's horrible. Instead of
function camera_connector_size(type)= type[6];
It would be
function camera_connector_size(type)= in_bounds(type, 6) ? type[6] : false;
Longer, slower, less readable.
On Wed, 29 Jul 2020 at 22:56, Ronaldo Persiano <rcmpersiano@gmail.com>
wrote:
> Well then it is a trade off between people that like to write
>> succinct code versus those that write bugs.
>
>
> Well, I don't like bugs. I prefer to be cautious than lazy. Anyway, isn't
> that succinct enough?
>
> function in_bounds(var,i) = is_list(var) && i>=0 && i<len(var);
> ...
> if(in_bounds(arr)) ...
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Wed, Jul 29, 2020 10:36 PM
Yeah, I'm with Ronaldo that I'd rather have a little bit more code than bugs.
function ind(list,i) = is_list(list) && i>=0 && i<len(i) && list[i]
? list[i] : 0;
function camera_connector_sizes(type) = ind(list,6);
This seems equally readable to me as list[6] and significantly less obscure.
Succinct is not always more readable or better when it means more cryptic or
obscure, and the form you prefer is definitely more obscure---someone
reading the code would have a harder time grasping the full implication, I
think.
The speed complaint is bogus. I already tested the speed consequences of
this and it saves less than a microsecond. Are you doing this a hundred
million times?
<quote author="nophead">
No it's horrible. Instead of
function camera_connector_size(type)= type[6];
It would be
function camera_connector_size(type)= in_bounds(type, 6) ? type[6] : false;
Longer, slower, less readable.
On Wed, 29 Jul 2020 at 22:56, Ronaldo Persiano <rcmpersiano@>
wrote:
Well then it is a trade off between people that like to write
succinct code versus those that write bugs.
Well, I don't like bugs. I prefer to be cautious than lazy. Anyway, isn't
that succinct enough?
function in_bounds(var,i) = is_list(var) && i>=0 && i<len(var);
Yeah, I'm with Ronaldo that I'd rather have a little bit more code than bugs.
function ind(list,i) = is_list(list) && i>=0 && i<len(i) && list[i]
? list[i] : 0;
function camera_connector_sizes(type) = ind(list,6);
This seems equally readable to me as list[6] and significantly less obscure.
Succinct is not always more readable or better when it means more cryptic or
obscure, and the form you prefer is definitely more obscure---someone
reading the code would have a harder time grasping the full implication, I
think.
The speed complaint is bogus. I already tested the speed consequences of
this and it saves less than a microsecond. Are you doing this a hundred
million times?
<quote author="nophead">
No it's horrible. Instead of
function camera_connector_size(type)= type[6];
It would be
function camera_connector_size(type)= in_bounds(type, 6) ? type[6] : false;
Longer, slower, less readable.
On Wed, 29 Jul 2020 at 22:56, Ronaldo Persiano <rcmpersiano@>
wrote:
> Well then it is a trade off between people that like to write
>> succinct code versus those that write bugs.
>
>
> Well, I don't like bugs. I prefer to be cautious than lazy. Anyway, isn't
> that succinct enough?
>
> function in_bounds(var,i) = is_list(var) && i>=0 && i<len(var);
> ...
> if(in_bounds(arr)) ...
> _______________________________________________
> 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/
NH
nop head
Thu, Jul 30, 2020 8:47 AM
Are you doing this a hundred million times?
Due to the bug in OpenSCAD where it evaluates every expression hundreds of
thousands of times the time taken to parse my code and instantiate modules
dominates after the first time where all the geometry is cached. Since
ind() would be a global function, I think each time it is called all my
global constants would get evaluated again.
Each operation might only take microseconds because PCs are basically very
fast but when there are explosions of complexity the time taken gets
exponential, making large projects impractical. I.e. there is a finite
limit to how big a project I can make and I am constantly looking for speed
optimisations. My current project has 1355 vitamins in it and probably only
half complete. Each one is described by a list and every property in the
list is read by an accessor function to isolate the list format. Of course
record structure with named fields would save a lot of this boilerplate
code.
On Wed, 29 Jul 2020 at 23:37, adrianv avm4@cornell.edu wrote:
Yeah, I'm with Ronaldo that I'd rather have a little bit more code than
bugs.
function ind(list,i) = is_list(list) && i>=0 && i<len(i) && list[i]
? list[i] : 0;
function camera_connector_sizes(type) = ind(list,6);
This seems equally readable to me as list[6] and significantly less
obscure.
Succinct is not always more readable or better when it means more cryptic
or
obscure, and the form you prefer is definitely more obscure---someone
reading the code would have a harder time grasping the full implication, I
think.
The speed complaint is bogus. I already tested the speed consequences of
this and it saves less than a microsecond. Are you doing this a hundred
million times?
<quote author="nophead">
No it's horrible. Instead of
function camera_connector_size(type)= type[6];
It would be
function camera_connector_size(type)= in_bounds(type, 6) ? type[6] : false;
Longer, slower, less readable.
On Wed, 29 Jul 2020 at 22:56, Ronaldo Persiano <rcmpersiano@>
wrote:
Well then it is a trade off between people that like to write
succinct code versus those that write bugs.
Well, I don't like bugs. I prefer to be cautious than lazy. Anyway, isn't
that succinct enough?
function in_bounds(var,i) = is_list(var) && i>=0 && i<len(var);
>Are you doing this a hundred million times?
Due to the bug in OpenSCAD where it evaluates every expression hundreds of
thousands of times the time taken to parse my code and instantiate modules
dominates after the first time where all the geometry is cached. Since
ind() would be a global function, I think each time it is called all my
global constants would get evaluated again.
Each operation might only take microseconds because PCs are basically very
fast but when there are explosions of complexity the time taken gets
exponential, making large projects impractical. I.e. there is a finite
limit to how big a project I can make and I am constantly looking for speed
optimisations. My current project has 1355 vitamins in it and probably only
half complete. Each one is described by a list and every property in the
list is read by an accessor function to isolate the list format. Of course
record structure with named fields would save a lot of this boilerplate
code.
On Wed, 29 Jul 2020 at 23:37, adrianv <avm4@cornell.edu> wrote:
> Yeah, I'm with Ronaldo that I'd rather have a little bit more code than
> bugs.
>
> function ind(list,i) = is_list(list) && i>=0 && i<len(i) && list[i]
> ? list[i] : 0;
>
> function camera_connector_sizes(type) = ind(list,6);
>
> This seems equally readable to me as list[6] and significantly less
> obscure.
> Succinct is not always more readable or better when it means more cryptic
> or
> obscure, and the form you prefer is definitely more obscure---someone
> reading the code would have a harder time grasping the full implication, I
> think.
>
> The speed complaint is bogus. I already tested the speed consequences of
> this and it saves less than a microsecond. Are you doing this a hundred
> million times?
>
> <quote author="nophead">
> No it's horrible. Instead of
>
> function camera_connector_size(type)= type[6];
>
> It would be
>
> function camera_connector_size(type)= in_bounds(type, 6) ? type[6] : false;
>
> Longer, slower, less readable.
>
>
>
> On Wed, 29 Jul 2020 at 22:56, Ronaldo Persiano <rcmpersiano@>
> wrote:
>
> > Well then it is a trade off between people that like to write
> >> succinct code versus those that write bugs.
> >
> >
> > Well, I don't like bugs. I prefer to be cautious than lazy. Anyway, isn't
> > that succinct enough?
> >
> > function in_bounds(var,i) = is_list(var) && i>=0 && i<len(var);
> > ...
> > if(in_bounds(arr)) ...
> > _______________________________________________
> > 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/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
RP
Ronaldo Persiano
Thu, Jul 30, 2020 10:27 AM
Due to the bug in OpenSCAD where it evaluates every expression hundreds of
thousands of times the time taken to parse my code and instantiate modules
dominates after the first time where all the geometry is cached. Since
ind() would be a global function, I think each time it is called all my
global constants would get evaluated again.
Yes, that is a huge problem. I don't intend to teach math to a calculus
teacher. I am just seeking a sensible way to get rid of the bug by using
both use<> and include<>. Isn't it possible in your case to put all those
small functions accessed everywhere in an used<> file and the global
constants in an included<> file?
>
>
> Due to the bug in OpenSCAD where it evaluates every expression hundreds of
> thousands of times the time taken to parse my code and instantiate modules
> dominates after the first time where all the geometry is cached. Since
> ind() would be a global function, I think each time it is called all my
> global constants would get evaluated again.
>
Yes, that is a huge problem. I don't intend to teach math to a calculus
teacher. I am just seeking a sensible way to get rid of the bug by using
both use<> and include<>. Isn't it possible in your case to put all those
small functions accessed everywhere in an used<> file and the global
constants in an included<> file?
A
adrianv
Thu, Jul 30, 2020 11:39 AM
Due to the bug in OpenSCAD where it evaluates every expression hundreds
of
thousands of times the time taken to parse my code and instantiate
modules
dominates after the first time where all the geometry is cached. Since
ind() would be a global function, I think each time it is called all my
global constants would get evaluated again.
Yes, that is a huge problem. I don't intend to teach math to a calculus
teacher. I am just seeking a sensible way to get rid of the bug by using
both use<> and include<>. Isn't it possible in your case to put all those
small functions accessed everywhere in an used<> file and the global
constants in an included<> file?
Yes, if you get 100k evaluations then it starts to add up to detectable
amounts of time.
I wonder would using functions instead of constants enable you to work
around the use bug? This would prevent recalculation.
--
Sent from: http://forum.openscad.org/
Ronaldo wrote
>>
>>
>> Due to the bug in OpenSCAD where it evaluates every expression hundreds
>> of
>> thousands of times the time taken to parse my code and instantiate
>> modules
>> dominates after the first time where all the geometry is cached. Since
>> ind() would be a global function, I think each time it is called all my
>> global constants would get evaluated again.
>>
>
> Yes, that is a huge problem. I don't intend to teach math to a calculus
> teacher. I am just seeking a sensible way to get rid of the bug by using
> both use<> and include<>. Isn't it possible in your case to put all those
> small functions accessed everywhere in an used<> file and the global
> constants in an included<> file?
Yes, if you get 100k evaluations then it starts to add up to detectable
amounts of time.
I wonder would using functions instead of constants enable you to work
around the use bug? This would prevent recalculation.
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Thu, Jul 30, 2020 12:09 PM
I wonder would using functions instead of constants enable you to work
around the use bug? This would prevent recalculation.
That would not work for calculated constants.
>
> I wonder would using functions instead of constants enable you to work
> around the use bug? This would prevent recalculation.
>
That would not work for calculated constants.
NH
nop head
Thu, Jul 30, 2020 12:27 PM
I have played around moving things between include and use and between
constants and functions. I think what l currently have is reasonably
optimal. The problem is what you gain on the swings you lose on the
roundabouts in both cases.
I do have all the global constants in includes but they have to be included
everywhere, so they are parsed and evaluated lots of times. I have moved
some expensive constants to functions to avoid them being re-evaluated but
then they get evaluated every time you use them, so it only gains if they
are not used very many times. And lots of constants depend on other
constants so wrapping them in functions starts to create a tree of function
calls and that soon explodes in time.
Similarly including files instead of using them avoids the constants being
evaluated lots of times due to calling into a used file but because they
are needed in more than one file you get them parsed and evaluated multiple
times. When they are used they are only parsed once and then cached.
The re-evaluation of constants actually placed a fundamental limit on
project complexity that doesn't exist in any other language I know.
Normally factoring sub expressions into constants makes the code faster and
shorter but in OpenSCAD it can make it much slower.
For example I made springs with sweep and since I wanted multiple springs
the same for battery contacts I made a function that returned the
polyhedron of the spring and stored it in a variable in the battery module
for reuse. That caused a slowdown because it got evaluated every time a
function in the battery module was called instead of just once and reused
for each battery. It is much faster to do the sweep again for each battery.
Totally counter intuitive.
On Thu, 30 Jul 2020, 12:40 adrianv, avm4@cornell.edu wrote:
Due to the bug in OpenSCAD where it evaluates every expression hundreds
of
thousands of times the time taken to parse my code and instantiate
modules
dominates after the first time where all the geometry is cached. Since
ind() would be a global function, I think each time it is called all my
global constants would get evaluated again.
Yes, that is a huge problem. I don't intend to teach math to a calculus
teacher. I am just seeking a sensible way to get rid of the bug by using
both use<> and include<>. Isn't it possible in your case to put all those
small functions accessed everywhere in an used<> file and the global
constants in an included<> file?
I have played around moving things between include and use and between
constants and functions. I think what l currently have is reasonably
optimal. The problem is what you gain on the swings you lose on the
roundabouts in both cases.
I do have all the global constants in includes but they have to be included
everywhere, so they are parsed and evaluated lots of times. I have moved
some expensive constants to functions to avoid them being re-evaluated but
then they get evaluated every time you use them, so it only gains if they
are not used very many times. And lots of constants depend on other
constants so wrapping them in functions starts to create a tree of function
calls and that soon explodes in time.
Similarly including files instead of using them avoids the constants being
evaluated lots of times due to calling into a used file but because they
are needed in more than one file you get them parsed and evaluated multiple
times. When they are used they are only parsed once and then cached.
The re-evaluation of constants actually placed a fundamental limit on
project complexity that doesn't exist in any other language I know.
Normally factoring sub expressions into constants makes the code faster and
shorter but in OpenSCAD it can make it much slower.
For example I made springs with sweep and since I wanted multiple springs
the same for battery contacts I made a function that returned the
polyhedron of the spring and stored it in a variable in the battery module
for reuse. That caused a slowdown because it got evaluated every time a
function in the battery module was called instead of just once and reused
for each battery. It is much faster to do the sweep again for each battery.
Totally counter intuitive.
On Thu, 30 Jul 2020, 12:40 adrianv, <avm4@cornell.edu> wrote:
> Ronaldo wrote
> >>
> >>
> >> Due to the bug in OpenSCAD where it evaluates every expression hundreds
> >> of
> >> thousands of times the time taken to parse my code and instantiate
> >> modules
> >> dominates after the first time where all the geometry is cached. Since
> >> ind() would be a global function, I think each time it is called all my
> >> global constants would get evaluated again.
> >>
> >
> > Yes, that is a huge problem. I don't intend to teach math to a calculus
> > teacher. I am just seeking a sensible way to get rid of the bug by using
> > both use<> and include<>. Isn't it possible in your case to put all those
> > small functions accessed everywhere in an used<> file and the global
> > constants in an included<> file?
>
> Yes, if you get 100k evaluations then it starts to add up to detectable
> amounts of time.
>
> I wonder would using functions instead of constants enable you to work
> around the use bug? This would prevent recalculation.
>
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
HL
Hans L
Thu, Jul 30, 2020 5:17 PM
nophead, I know you have posted before about this performance issue for
large projects. I believe you that it's a major problem for your use case,
but it's difficult for me to reproduce.
Could you open an issue with a clear list of steps to reproduce?
Normally a self contained single file test case is preferable, but due to
the nature of this problem it would be fine if the steps to reproduce are
something like:
- Download some public repo
- Preview some top level file which exemplifies the poor performance you
encounter
It's possible there are multiple issues at play here, so it would be useful
to do some profiling on such a case and verify where time is being spent.
On Thu, Jul 30, 2020 at 7:29 AM nop head nop.head@gmail.com wrote:
I have played around moving things between include and use and between
constants and functions. I think what l currently have is reasonably
optimal. The problem is what you gain on the swings you lose on the
roundabouts in both cases.
I do have all the global constants in includes but they have to be
included everywhere, so they are parsed and evaluated lots of times. I have
moved some expensive constants to functions to avoid them being
re-evaluated but then they get evaluated every time you use them, so it
only gains if they are not used very many times. And lots of constants
depend on other constants so wrapping them in functions starts to create a
tree of function calls and that soon explodes in time.
Similarly including files instead of using them avoids the constants being
evaluated lots of times due to calling into a used file but because they
are needed in more than one file you get them parsed and evaluated multiple
times. When they are used they are only parsed once and then cached.
The re-evaluation of constants actually placed a fundamental limit on
project complexity that doesn't exist in any other language I know.
Normally factoring sub expressions into constants makes the code faster and
shorter but in OpenSCAD it can make it much slower.
For example I made springs with sweep and since I wanted multiple springs
the same for battery contacts I made a function that returned the
polyhedron of the spring and stored it in a variable in the battery module
for reuse. That caused a slowdown because it got evaluated every time a
function in the battery module was called instead of just once and reused
for each battery. It is much faster to do the sweep again for each battery.
Totally counter intuitive.
On Thu, 30 Jul 2020, 12:40 adrianv, avm4@cornell.edu wrote:
Due to the bug in OpenSCAD where it evaluates every expression hundreds
of
thousands of times the time taken to parse my code and instantiate
modules
dominates after the first time where all the geometry is cached. Since
ind() would be a global function, I think each time it is called all my
global constants would get evaluated again.
Yes, that is a huge problem. I don't intend to teach math to a calculus
teacher. I am just seeking a sensible way to get rid of the bug by using
both use<> and include<>. Isn't it possible in your case to put all
small functions accessed everywhere in an used<> file and the global
constants in an included<> file?
nophead, I know you have posted before about this performance issue for
large projects. I believe you that it's a major problem for your use case,
but it's difficult for me to reproduce.
Could you open an issue with a clear list of steps to reproduce?
Normally a self contained single file test case is preferable, but due to
the nature of this problem it would be fine if the steps to reproduce are
something like:
1) Download some public repo
2) Preview some top level file which exemplifies the poor performance you
encounter
It's possible there are multiple issues at play here, so it would be useful
to do some profiling on such a case and verify where time is being spent.
On Thu, Jul 30, 2020 at 7:29 AM nop head <nop.head@gmail.com> wrote:
> I have played around moving things between include and use and between
> constants and functions. I think what l currently have is reasonably
> optimal. The problem is what you gain on the swings you lose on the
> roundabouts in both cases.
>
> I do have all the global constants in includes but they have to be
> included everywhere, so they are parsed and evaluated lots of times. I have
> moved some expensive constants to functions to avoid them being
> re-evaluated but then they get evaluated every time you use them, so it
> only gains if they are not used very many times. And lots of constants
> depend on other constants so wrapping them in functions starts to create a
> tree of function calls and that soon explodes in time.
>
> Similarly including files instead of using them avoids the constants being
> evaluated lots of times due to calling into a used file but because they
> are needed in more than one file you get them parsed and evaluated multiple
> times. When they are used they are only parsed once and then cached.
>
> The re-evaluation of constants actually placed a fundamental limit on
> project complexity that doesn't exist in any other language I know.
> Normally factoring sub expressions into constants makes the code faster and
> shorter but in OpenSCAD it can make it much slower.
>
> For example I made springs with sweep and since I wanted multiple springs
> the same for battery contacts I made a function that returned the
> polyhedron of the spring and stored it in a variable in the battery module
> for reuse. That caused a slowdown because it got evaluated every time a
> function in the battery module was called instead of just once and reused
> for each battery. It is much faster to do the sweep again for each battery.
> Totally counter intuitive.
>
> On Thu, 30 Jul 2020, 12:40 adrianv, <avm4@cornell.edu> wrote:
>
>> Ronaldo wrote
>> >>
>> >>
>> >> Due to the bug in OpenSCAD where it evaluates every expression hundreds
>> >> of
>> >> thousands of times the time taken to parse my code and instantiate
>> >> modules
>> >> dominates after the first time where all the geometry is cached. Since
>> >> ind() would be a global function, I think each time it is called all my
>> >> global constants would get evaluated again.
>> >>
>> >
>> > Yes, that is a huge problem. I don't intend to teach math to a calculus
>> > teacher. I am just seeking a sensible way to get rid of the bug by using
>> > both use<> and include<>. Isn't it possible in your case to put all
>> those
>> > small functions accessed everywhere in an used<> file and the global
>> > constants in an included<> file?
>>
>> Yes, if you get 100k evaluations then it starts to add up to detectable
>> amounts of time.
>>
>> I wonder would using functions instead of constants enable you to work
>> around the use bug? This would prevent recalculation.
>>
>>
>>
>>
>>
>> --
>> Sent from: http://forum.openscad.org/
>>
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Thu, Jul 30, 2020 6:36 PM
When I noticed this issue last year I posted an example here
http://forum.openscad.org/variable-definition-in-used-file-cause-huge-run-time-hit-td26001.html
This problem is very simple. It doesn't require fancy profiling or a big
setup. It's trivial to reproduce and doesn't require any thing complicated.
The problem is simply that if you "use" a file and then call a function
whose definition is in the used file, then all variable definitions at the
top level in the used file get (re)executed. And they get executed every
time you run any function in the file. No big deal if it's a=3 and you
call the function a couple times, but if it's a complex calculation, or you
call functions hundreds or thousands of times it gets to be a big drain on
performance. My understanding is that in nophead's projects, file are used a
lot and he has lots of variable definitions, so it ends up being a big
problem. He also gave the example of wanting to build a big polyhedron once
and save the result in a variable. Can't do it because the polyhedron gets
recalculated every time any function in the file is run.
See https://github.com/openscad/openscad/issues/782. Linde, who opened the
issue in 2014 says: "Fixing the issue is easy, but I've not attached a patch
as there is a remote chance that the behavior is intended, and because there
is also a remote chance of this breaking / changing the behavior of some
existing code that could be relying on this."
Here's an even simpler example than my original one:
file.scad:
use<test.scad>
output = [for(i=[1:100]) test(i)];
echo(output);
test.scad:
a = [for(i=[1:1e5]) sin(i)];
b = echo("evaluating b") 3;
function test(x) = x;
If I run this it takes 9s to run and the "evaluating b" message displays 100
times. If I either change "use" to include it runs in 0s and "evaluating b"
displays only once. Or if I use "use" but comment out the time consuming
definition of a then it also runs in 0s. In my original example I had the
run time go from 5s up to
9 minutes in code that was derived from a real case, where the definitions
in the lib file were only slightly expensive but functions in the file were
called thousands of times.
thehans wrote
nophead, I know you have posted before about this performance issue for
large projects. I believe you that it's a major problem for your use
case,
but it's difficult for me to reproduce.
Could you open an issue with a clear list of steps to reproduce?
Normally a self contained single file test case is preferable, but due to
the nature of this problem it would be fine if the steps to reproduce are
something like:
- Download some public repo
- Preview some top level file which exemplifies the poor performance you
encounter
It's possible there are multiple issues at play here, so it would be
useful
to do some profiling on such a case and verify where time is being spent.
On Thu, Jul 30, 2020 at 7:29 AM nop head <
I have played around moving things between include and use and between
constants and functions. I think what l currently have is reasonably
optimal. The problem is what you gain on the swings you lose on the
roundabouts in both cases.
I do have all the global constants in includes but they have to be
included everywhere, so they are parsed and evaluated lots of times. I
have
moved some expensive constants to functions to avoid them being
re-evaluated but then they get evaluated every time you use them, so it
only gains if they are not used very many times. And lots of constants
depend on other constants so wrapping them in functions starts to create
a
tree of function calls and that soon explodes in time.
Similarly including files instead of using them avoids the constants
being
evaluated lots of times due to calling into a used file but because they
are needed in more than one file you get them parsed and evaluated
multiple
times. When they are used they are only parsed once and then cached.
The re-evaluation of constants actually placed a fundamental limit on
project complexity that doesn't exist in any other language I know.
Normally factoring sub expressions into constants makes the code faster
and
shorter but in OpenSCAD it can make it much slower.
For example I made springs with sweep and since I wanted multiple springs
the same for battery contacts I made a function that returned the
polyhedron of the spring and stored it in a variable in the battery
module
for reuse. That caused a slowdown because it got evaluated every time a
function in the battery module was called instead of just once and reused
for each battery. It is much faster to do the sweep again for each
battery.
Totally counter intuitive.
On Thu, 30 Jul 2020, 12:40 adrianv, <
Due to the bug in OpenSCAD where it evaluates every expression
of
thousands of times the time taken to parse my code and instantiate
modules
dominates after the first time where all the geometry is cached.
ind() would be a global function, I think each time it is called all
global constants would get evaluated again.
Yes, that is a huge problem. I don't intend to teach math to a
teacher. I am just seeking a sensible way to get rid of the bug by
both use<> and include<>. Isn't it possible in your case to put all
small functions accessed everywhere in an used<> file and the global
constants in an included<> file?
Yes, if you get 100k evaluations then it starts to add up to detectable
amounts of time.
I wonder would using functions instead of constants enable you to work
around the use bug? This would prevent recalculation.
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
When I noticed this issue last year I posted an example here
http://forum.openscad.org/variable-definition-in-used-file-cause-huge-run-time-hit-td26001.html
This problem is very simple. It doesn't require fancy profiling or a big
setup. It's trivial to reproduce and doesn't require any thing complicated.
The problem is simply that if you "use" a file and then call a function
whose definition is in the used file, then all variable definitions at the
top level in the used file get (re)executed. And they get executed every
time you run any function in the file. No big deal if it's a=3 and you
call the function a couple times, but if it's a complex calculation, or you
call functions hundreds or thousands of times it gets to be a big drain on
performance. My understanding is that in nophead's projects, file are used a
lot and he has lots of variable definitions, so it ends up being a big
problem. He also gave the example of wanting to build a big polyhedron once
and save the result in a variable. Can't do it because the polyhedron gets
recalculated every time any function in the file is run.
See https://github.com/openscad/openscad/issues/782. Linde, who opened the
issue in 2014 says: "Fixing the issue is easy, but I've not attached a patch
as there is a remote chance that the behavior is intended, and because there
is also a remote chance of this breaking / changing the behavior of some
existing code that could be relying on this."
Here's an even simpler example than my original one:
file.scad:
use<test.scad>
output = [for(i=[1:100]) test(i)];
echo(output);
test.scad:
a = [for(i=[1:1e5]) sin(i)];
b = echo("evaluating b") 3;
function test(x) = x;
If I run this it takes 9s to run and the "evaluating b" message displays 100
times. If I either change "use" to include it runs in 0s and "evaluating b"
displays only once. Or if I use "use" but comment out the time consuming
definition of a then it also runs in 0s. In my original example I had the
run time go from 5s up to
9 minutes in code that was derived from a real case, where the definitions
in the lib file were only slightly expensive but functions in the file were
called thousands of times.
thehans wrote
> nophead, I know you have posted before about this performance issue for
> large projects. I believe you that it's a major problem for your use
> case,
> but it's difficult for me to reproduce.
> Could you open an issue with a clear list of steps to reproduce?
> Normally a self contained single file test case is preferable, but due to
> the nature of this problem it would be fine if the steps to reproduce are
> something like:
> 1) Download some public repo
> 2) Preview some top level file which exemplifies the poor performance you
> encounter
>
> It's possible there are multiple issues at play here, so it would be
> useful
> to do some profiling on such a case and verify where time is being spent.
>
>
> On Thu, Jul 30, 2020 at 7:29 AM nop head <
> nop.head@
> > wrote:
>
>> I have played around moving things between include and use and between
>> constants and functions. I think what l currently have is reasonably
>> optimal. The problem is what you gain on the swings you lose on the
>> roundabouts in both cases.
>>
>> I do have all the global constants in includes but they have to be
>> included everywhere, so they are parsed and evaluated lots of times. I
>> have
>> moved some expensive constants to functions to avoid them being
>> re-evaluated but then they get evaluated every time you use them, so it
>> only gains if they are not used very many times. And lots of constants
>> depend on other constants so wrapping them in functions starts to create
>> a
>> tree of function calls and that soon explodes in time.
>>
>> Similarly including files instead of using them avoids the constants
>> being
>> evaluated lots of times due to calling into a used file but because they
>> are needed in more than one file you get them parsed and evaluated
>> multiple
>> times. When they are used they are only parsed once and then cached.
>>
>> The re-evaluation of constants actually placed a fundamental limit on
>> project complexity that doesn't exist in any other language I know.
>> Normally factoring sub expressions into constants makes the code faster
>> and
>> shorter but in OpenSCAD it can make it much slower.
>>
>> For example I made springs with sweep and since I wanted multiple springs
>> the same for battery contacts I made a function that returned the
>> polyhedron of the spring and stored it in a variable in the battery
>> module
>> for reuse. That caused a slowdown because it got evaluated every time a
>> function in the battery module was called instead of just once and reused
>> for each battery. It is much faster to do the sweep again for each
>> battery.
>> Totally counter intuitive.
>>
>> On Thu, 30 Jul 2020, 12:40 adrianv, <
> avm4@
> > wrote:
>>
>>> Ronaldo wrote
>>> >>
>>> >>
>>> >> Due to the bug in OpenSCAD where it evaluates every expression
>>> hundreds
>>> >> of
>>> >> thousands of times the time taken to parse my code and instantiate
>>> >> modules
>>> >> dominates after the first time where all the geometry is cached.
>>> Since
>>> >> ind() would be a global function, I think each time it is called all
>>> my
>>> >> global constants would get evaluated again.
>>> >>
>>> >
>>> > Yes, that is a huge problem. I don't intend to teach math to a
>>> calculus
>>> > teacher. I am just seeking a sensible way to get rid of the bug by
>>> using
>>> > both use<> and include<>. Isn't it possible in your case to put all
>>> those
>>> > small functions accessed everywhere in an used<> file and the global
>>> > constants in an included<> file?
>>>
>>> Yes, if you get 100k evaluations then it starts to add up to detectable
>>> amounts of time.
>>>
>>> I wonder would using functions instead of constants enable you to work
>>> around the use bug? This would prevent recalculation.
>>>
>>>
>>>
>>>
>>>
>>> --
>>> Sent from: http://forum.openscad.org/
>>>
>>> _______________________________________________
>>> 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
>>
>
> _______________________________________________
> 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, Jul 30, 2020 6:47 PM
The only project large enough for the effect to be noticeable is my half
finished 3D printer, which I don't particularly want to publish in its
current state.
The effect can be seen with the tiny example project bundled with my
library. If you add function echo to one of the global constant
definitions, you can see it gets defined 951 times despite the project only
using a handful of files in the library that include the global
definitions. In the small example it isn't noticeable because as
adrianv said each definition only takes a few us. The problem is every
function call or module call into the library causes all the global
constants to be reevaluated. So even for this tiny project that adds up to
nearly 1000 times.
As project complexity grows the number of calls into the library grows,
especially if there are some loops, for example making a 2D grill with
poly_circles. Because poly_circle() is in a different module to grill() it
means for every single hole, all the global constants are evaluated again.
Even for moderate size projects it still isn't a big overhead, because it
is very fast but on my large project that is split into lots of separate
files it completely explodes because not only does every library call
evaluate every constant, every call between projects files evaluates every
constant in those as well. That includes the thousands of object definition
lists in the library and also much more expensive constant expressions that
work out positions of things depending on other things in the project. It
was ending up taking a minute to evaluate all the expressions until I added
some hacks such as storing an expensive expression in a $variable, setting
it at the start of main.scad and then including all the other files that
see if it is set and use it or calculate it themselves if they have been
opened standalone. They should simply of been able to use the file that
makes the list once, stores it in a variable, and exports it with a
function.
- Download https://github.com/nophead/NopSCADlib
- Edit this line
https://github.com/nophead/NopSCADlib/blob/master/global_defs.scad#L48 and
change it to
inf = echo("inf") 1e10; // very big
- Preview
https://github.com/nophead/NopSCADlib/blob/master/examples/MainsBreakOutBox/scad/bob_main.scad
and
count how many times you see ECHO: "inf"
The complication is that constant espressions can include $variables and
then they need reevaluating for each value of the $variable.
On Thu, 30 Jul 2020 at 18:18, Hans L thehans@gmail.com wrote:
nophead, I know you have posted before about this performance issue for
large projects. I believe you that it's a major problem for your use case,
but it's difficult for me to reproduce.
Could you open an issue with a clear list of steps to reproduce?
Normally a self contained single file test case is preferable, but due to
the nature of this problem it would be fine if the steps to reproduce are
something like:
- Download some public repo
- Preview some top level file which exemplifies the poor performance you
encounter
It's possible there are multiple issues at play here, so it would be
useful to do some profiling on such a case and verify where time is being
spent.
On Thu, Jul 30, 2020 at 7:29 AM nop head nop.head@gmail.com wrote:
I have played around moving things between include and use and between
constants and functions. I think what l currently have is reasonably
optimal. The problem is what you gain on the swings you lose on the
roundabouts in both cases.
I do have all the global constants in includes but they have to be
included everywhere, so they are parsed and evaluated lots of times. I have
moved some expensive constants to functions to avoid them being
re-evaluated but then they get evaluated every time you use them, so it
only gains if they are not used very many times. And lots of constants
depend on other constants so wrapping them in functions starts to create a
tree of function calls and that soon explodes in time.
Similarly including files instead of using them avoids the constants
being evaluated lots of times due to calling into a used file but because
they are needed in more than one file you get them parsed and evaluated
multiple times. When they are used they are only parsed once and then
cached.
The re-evaluation of constants actually placed a fundamental limit on
project complexity that doesn't exist in any other language I know.
Normally factoring sub expressions into constants makes the code faster and
shorter but in OpenSCAD it can make it much slower.
For example I made springs with sweep and since I wanted multiple springs
the same for battery contacts I made a function that returned the
polyhedron of the spring and stored it in a variable in the battery module
for reuse. That caused a slowdown because it got evaluated every time a
function in the battery module was called instead of just once and reused
for each battery. It is much faster to do the sweep again for each battery.
Totally counter intuitive.
On Thu, 30 Jul 2020, 12:40 adrianv, avm4@cornell.edu wrote:
Due to the bug in OpenSCAD where it evaluates every expression
of
thousands of times the time taken to parse my code and instantiate
modules
dominates after the first time where all the geometry is cached. Since
ind() would be a global function, I think each time it is called all
global constants would get evaluated again.
Yes, that is a huge problem. I don't intend to teach math to a calculus
teacher. I am just seeking a sensible way to get rid of the bug by
both use<> and include<>. Isn't it possible in your case to put all
small functions accessed everywhere in an used<> file and the global
constants in an included<> file?
The only project large enough for the effect to be noticeable is my half
finished 3D printer, which I don't particularly want to publish in its
current state.
The effect can be seen with the tiny example project bundled with my
library. If you add function echo to one of the global constant
definitions, you can see it gets defined 951 times despite the project only
using a handful of files in the library that include the global
definitions. In the small example it isn't noticeable because as
adrianv said each definition only takes a few us. The problem is every
function call or module call into the library causes all the global
constants to be reevaluated. So even for this tiny project that adds up to
nearly 1000 times.
As project complexity grows the number of calls into the library grows,
especially if there are some loops, for example making a 2D grill with
poly_circles. Because poly_circle() is in a different module to grill() it
means for every single hole, all the global constants are evaluated again.
Even for moderate size projects it still isn't a big overhead, because it
is very fast but on my large project that is split into lots of separate
files it completely explodes because not only does every library call
evaluate every constant, every call between projects files evaluates every
constant in those as well. That includes the thousands of object definition
lists in the library and also much more expensive constant expressions that
work out positions of things depending on other things in the project. It
was ending up taking a minute to evaluate all the expressions until I added
some hacks such as storing an expensive expression in a $variable, setting
it at the start of main.scad and then including all the other files that
see if it is set and use it or calculate it themselves if they have been
opened standalone. They should simply of been able to use the file that
makes the list once, stores it in a variable, and exports it with a
function.
1) Download https://github.com/nophead/NopSCADlib
2) Edit this line
https://github.com/nophead/NopSCADlib/blob/master/global_defs.scad#L48 and
change it to
inf = echo("inf") 1e10; // very big
3) Preview
https://github.com/nophead/NopSCADlib/blob/master/examples/MainsBreakOutBox/scad/bob_main.scad
and
count how many times you see ECHO: "inf"
The complication is that constant espressions can include $variables and
then they need reevaluating for each value of the $variable.
On Thu, 30 Jul 2020 at 18:18, Hans L <thehans@gmail.com> wrote:
> nophead, I know you have posted before about this performance issue for
> large projects. I believe you that it's a major problem for your use case,
> but it's difficult for me to reproduce.
> Could you open an issue with a clear list of steps to reproduce?
> Normally a self contained single file test case is preferable, but due to
> the nature of this problem it would be fine if the steps to reproduce are
> something like:
> 1) Download some public repo
> 2) Preview some top level file which exemplifies the poor performance you
> encounter
>
> It's possible there are multiple issues at play here, so it would be
> useful to do some profiling on such a case and verify where time is being
> spent.
>
>
> On Thu, Jul 30, 2020 at 7:29 AM nop head <nop.head@gmail.com> wrote:
>
>> I have played around moving things between include and use and between
>> constants and functions. I think what l currently have is reasonably
>> optimal. The problem is what you gain on the swings you lose on the
>> roundabouts in both cases.
>>
>> I do have all the global constants in includes but they have to be
>> included everywhere, so they are parsed and evaluated lots of times. I have
>> moved some expensive constants to functions to avoid them being
>> re-evaluated but then they get evaluated every time you use them, so it
>> only gains if they are not used very many times. And lots of constants
>> depend on other constants so wrapping them in functions starts to create a
>> tree of function calls and that soon explodes in time.
>>
>> Similarly including files instead of using them avoids the constants
>> being evaluated lots of times due to calling into a used file but because
>> they are needed in more than one file you get them parsed and evaluated
>> multiple times. When they are used they are only parsed once and then
>> cached.
>>
>> The re-evaluation of constants actually placed a fundamental limit on
>> project complexity that doesn't exist in any other language I know.
>> Normally factoring sub expressions into constants makes the code faster and
>> shorter but in OpenSCAD it can make it much slower.
>>
>> For example I made springs with sweep and since I wanted multiple springs
>> the same for battery contacts I made a function that returned the
>> polyhedron of the spring and stored it in a variable in the battery module
>> for reuse. That caused a slowdown because it got evaluated every time a
>> function in the battery module was called instead of just once and reused
>> for each battery. It is much faster to do the sweep again for each battery.
>> Totally counter intuitive.
>>
>> On Thu, 30 Jul 2020, 12:40 adrianv, <avm4@cornell.edu> wrote:
>>
>>> Ronaldo wrote
>>> >>
>>> >>
>>> >> Due to the bug in OpenSCAD where it evaluates every expression
>>> hundreds
>>> >> of
>>> >> thousands of times the time taken to parse my code and instantiate
>>> >> modules
>>> >> dominates after the first time where all the geometry is cached. Since
>>> >> ind() would be a global function, I think each time it is called all
>>> my
>>> >> global constants would get evaluated again.
>>> >>
>>> >
>>> > Yes, that is a huge problem. I don't intend to teach math to a calculus
>>> > teacher. I am just seeking a sensible way to get rid of the bug by
>>> using
>>> > both use<> and include<>. Isn't it possible in your case to put all
>>> those
>>> > small functions accessed everywhere in an used<> file and the global
>>> > constants in an included<> file?
>>>
>>> Yes, if you get 100k evaluations then it starts to add up to detectable
>>> amounts of time.
>>>
>>> I wonder would using functions instead of constants enable you to work
>>> around the use bug? This would prevent recalculation.
>>>
>>>
>>>
>>>
>>>
>>> --
>>> Sent from: http://forum.openscad.org/
>>>
>>> _______________________________________________
>>> OpenSCAD mailing list
>>> Discuss@lists.openscad.org
>>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>>
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Thu, Jul 30, 2020 7:45 PM
I wonder would using functions instead of constants enable you to work
around the use bug? This would prevent recalculation.
That would not work for calculated constants.
Yes it would, as long as recalculating the constants as needed was (much)
faster than recalculating every time a function in the library runs. If
we're talking about hundreds of thousands of re-evaluations due the the
"use" then it's entirely possible that full recalculation as needed would be
faster.
--
Sent from: http://forum.openscad.org/
Ronaldo wrote
>>
>> I wonder would using functions instead of constants enable you to work
>> around the use bug? This would prevent recalculation.
>>
>
> That would not work for calculated constants.
Yes it would, as long as recalculating the constants as needed was (much)
faster than recalculating every time a function in the library runs. If
we're talking about hundreds of thousands of re-evaluations due the the
"use" then it's entirely possible that full recalculation as needed would be
faster.
--
Sent from: http://forum.openscad.org/
O
OzAtMichael
Fri, Jul 31, 2020 3:31 AM
And len should go back to silently returning undef when applied to a scalar.
+1
One of the really good things about OpenSCAD is its simplicity.
Beginners can get started with uncomplicated code generating straight forward objects.
But there is also much sophistication that can be used once experienced enough.
Adding strong checks then requires complexity, Nopheads ' if(is_list(type) && len(type) > 3 &&
type[3])' example shows this.
See how other languages draw a cube: https://rosettacode.org/wiki/Draw_a_cuboid
There are plenty of fully formed languages with strong typing and all the extra complexity
(https://en.wikipedia.org/wiki/Syntactic_sugar#Syntactic_salt)
We have been able to add more features like list comprehensions, assert, advanced echo, which are
useful for advanced programmers, but which the beginner can ignore.
The change to len() nophead mentions was the start of forcing additional complexity.
If you want a language which forces the programmer to write code that enforces ridged safe
programming, there are plenty of them to choose.
From: Discuss [mailto:discuss-bounces@lists.openscad.org] On Behalf Of nop head
Sent: Thu, 30 Jul 2020 04:57
To: OpenSCAD general discussion
Subject: Re: [OpenSCAD] What happened to booleans?
The issue is that OpenSCAD has been used for many years and has always returned undef for an out of
bounds index so I use it as a shortcut. Instead of if(len(type) > 3 && type[3]) is just
if(type[3]), which is a lot less ugly. It saves adding empty values to the end of my object
descriptors and makes the lack of an entry the same as an empty entry, which makes semantic sense
to me. And len should go back to silently returning undef when applied to a scalar. Otherwise it
becomes if(is_list(type) && len(type) > 3 && type[3]), which is even more ugly. In practice the
list element is wrapped in an accessor function, so the intent becomes obvious. E.g.
conn = camera_connector_size(type);
if(conn) {
use conn to draw the connector
}
I doubt there are many cases where people do if(list[n]) where it is a mistake, using list[n] to
compute something or compare it to anything other than undef is definitely an error if the list
isn't that long and my proposal would catch all other errors due to bad indexing.
If an undef value goes down an execution path that doesn't use it then it isn't a bug as far as I
am concerned. undef exists as a value, can be assigned to variables and passed as parameters and
silently tested as false with if, so I don't think that should change. But if it always stores how
it became undef then debugging is easy because if you ever use it in an expression you will get a
warning showing where it became undef and where it was finally used. Exactly what you need to
debug.
Whether operators get overloaded to work on strings in the future doesn't matter. At the moment
they don't so you get undef.
On Wed, 29 Jul 2020 at 19:09, Jordan Brown openscad@jordan.maileater.net wrote:
[ Please forgive my tendency to write a lot about this. It's actually a primary interest in my
professional life - how to write programs to make it difficult to have bugs, how to detect the bugs
you can't prevent, and how to design languages, libraries, and other infrastructure so as to
prevent and detect bugs. A related topic is "how to report errors so that the error reports are
useful". ]
On 7/29/2020 12:10 AM, Rogier Wolff wrote:
Some "failures" are not failures, [...]
So... when talking abstractly: "You should throw errors as close as possible to the line causing
the problem." I'm all for it.... But when you think about it, it is not as clear cut as you might
think.
Yes. Distinguishing a likely bug from an "expected" error is important.
Nop head's "when you need a concrete answer" test is certainly a good start. But I don't think
it's quite enough. The next question is "what is a likely bug, and what is merely missing data?".
A missing parameter (in OpenSCAD, where missing parameters are expected) is clearly not an error.
(But maybe OpenSCAD should have a syntax that says "this parameter is required".)
An attempt to divide by zero is clearly an error. In the unlikely event that you really want
something infinity-like as the result, require the programmer to explicitly check first. (atan2()
comes to mind.)
I think it's clear that if a function requires a particular parameter, then not supplying it is an
error. (But not everybody agrees. In C, I often see people checking required parameters for NULL
and returning an error if they're NULL. No! The parameter is required; if the caller supplied a
NULL then the caller unquestionably has a bug!)
But an array index that's out of bounds? Hard to say. You might say it's a bug, or you might say
that returning undef allows the program to probe for the dimensions of the array. I would come
down on the side of calling it an error. If you want to probe for the dimensions of the array,
you can use len() and then the intent is obvious. That's a key thing: having the program convey
the programmer's intent. If you have out-of-bounds references simply return undef, neither the
interpreter nor a reviewer can tell what the intent is, and so you can't detect the bug. If, on
the other hand, you consider it an error, then you have two distinct idioms: if your intent is to
retrieve a value (and you don't expect your index to be out of bounds) then you do a simple
reference; if your intent is to check for the presence of the value then you use len(). Yes, if
"undef" was a reasonable default value you have to go a bit out of your way and say i < len(x) ?
x[i] : undef, but that doesn't seem like an onerous price to pay for protecting all of the other
cases.
Mixed-type expressions are particularly tricky. There are reasonable definitions for many
combinations. I'd draw the line at the point where you have to "stretch" to say that the
definition is reasonable. String concatenation through '+' is reasonable. String replication
through multiplication seems reasonable. Saying that boolean*string is equivalent to (boolean ?
string : "") seems past the limit. Automatic to-string when you mix a string and something else
with "+"... I'm not sure. It's reasonably obvious and awfully convenient, but is implicated in any
number of cases of "null" appearing in user interfaces.
Hans's "undef with a reason" does help a lot. I'm not sure it's enough. Because undef is
considered false for "if" purposes (a definition that seems unlikely to change) an undef that came
from a bug can lead to silently going down an execution path that doesn't happen to ever use the
value in a "concrete" way.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
This email has been checked for viruses by AVG.
https://www.avg.com
> And len should go back to silently returning undef when applied to a scalar.
+1
One of the really good things about OpenSCAD is its simplicity.
Beginners can get started with uncomplicated code generating straight forward objects.
But there is also much sophistication that can be used once experienced enough.
Adding strong checks then requires complexity, Nopheads ' if(is_list(type) && len(type) > 3 &&
type[3])' example shows this.
See how other languages draw a cube: https://rosettacode.org/wiki/Draw_a_cuboid
There are plenty of fully formed languages with strong typing and all the extra complexity
(https://en.wikipedia.org/wiki/Syntactic_sugar#Syntactic_salt)
We have been able to add more features like list comprehensions, assert, advanced echo, which are
useful for advanced programmers, but which the beginner can ignore.
The change to len() nophead mentions was the start of forcing additional complexity.
If you want a language which forces the programmer to write code that enforces ridged safe
programming, there are plenty of them to choose.
_____
From: Discuss [mailto:discuss-bounces@lists.openscad.org] On Behalf Of nop head
Sent: Thu, 30 Jul 2020 04:57
To: OpenSCAD general discussion
Subject: Re: [OpenSCAD] What happened to booleans?
The issue is that OpenSCAD has been used for many years and has always returned undef for an out of
bounds index so I use it as a shortcut. Instead of if(len(type) > 3 && type[3]) is just
if(type[3]), which is a lot less ugly. It saves adding empty values to the end of my object
descriptors and makes the lack of an entry the same as an empty entry, which makes semantic sense
to me. And len should go back to silently returning undef when applied to a scalar. Otherwise it
becomes if(is_list(type) && len(type) > 3 && type[3]), which is even more ugly. In practice the
list element is wrapped in an accessor function, so the intent becomes obvious. E.g.
conn = camera_connector_size(type);
if(conn) {
use conn to draw the connector
}
I doubt there are many cases where people do if(list[n]) where it is a mistake, using list[n] to
compute something or compare it to anything other than undef is definitely an error if the list
isn't that long and my proposal would catch all other errors due to bad indexing.
If an undef value goes down an execution path that doesn't use it then it isn't a bug as far as I
am concerned. undef exists as a value, can be assigned to variables and passed as parameters and
silently tested as false with if, so I don't think that should change. But if it always stores how
it became undef then debugging is easy because if you ever use it in an expression you will get a
warning showing where it became undef and where it was finally used. Exactly what you need to
debug.
Whether operators get overloaded to work on strings in the future doesn't matter. At the moment
they don't so you get undef.
On Wed, 29 Jul 2020 at 19:09, Jordan Brown <openscad@jordan.maileater.net> wrote:
[ Please forgive my tendency to write a lot about this. It's actually a primary interest in my
professional life - how to write programs to make it difficult to have bugs, how to detect the bugs
you can't prevent, and how to design languages, libraries, and other infrastructure so as to
prevent and detect bugs. A related topic is "how to report errors so that the error reports are
useful". ]
On 7/29/2020 12:10 AM, Rogier Wolff wrote:
Some "failures" are not failures, [...]
So... when talking abstractly: "You should throw errors as close as possible to the line causing
the problem." I'm all for it.... But when you think about it, it is not as clear cut as you might
think.
Yes. Distinguishing a likely bug from an "expected" error is important.
Nop head's "when you need a concrete answer" test is certainly a good start. But I don't think
it's quite enough. The next question is "what is a likely bug, and what is merely missing data?".
A missing parameter (in OpenSCAD, where missing parameters are expected) is clearly not an error.
(But maybe OpenSCAD should have a syntax that says "this parameter is required".)
An attempt to divide by zero is clearly an error. In the unlikely event that you really want
something infinity-like as the result, require the programmer to explicitly check first. (atan2()
comes to mind.)
I think it's clear that if a function requires a particular parameter, then not supplying it is an
error. (But not everybody agrees. In C, I often see people checking required parameters for NULL
and returning an error if they're NULL. No! The parameter is required; if the caller supplied a
NULL then the caller unquestionably has a bug!)
But an array index that's out of bounds? Hard to say. You might say it's a bug, or you might say
that returning undef allows the program to probe for the dimensions of the array. I would come
down on the side of calling it an error. If you *want* to probe for the dimensions of the array,
you can use len() and then the intent is obvious. That's a key thing: having the program convey
the programmer's intent. If you have out-of-bounds references simply return undef, neither the
interpreter nor a reviewer can tell what the intent is, and so you can't detect the bug. If, on
the other hand, you consider it an error, then you have two distinct idioms: if your intent is to
retrieve a value (and you don't expect your index to be out of bounds) then you do a simple
reference; if your intent is to check for the presence of the value then you use len(). Yes, if
"undef" was a reasonable default value you have to go a bit out of your way and say i < len(x) ?
x[i] : undef, but that doesn't seem like an onerous price to pay for protecting all of the *other*
cases.
Mixed-type expressions are particularly tricky. There are reasonable definitions for many
combinations. I'd draw the line at the point where you have to "stretch" to say that the
definition is reasonable. String concatenation through '+' is reasonable. String replication
through multiplication seems reasonable. Saying that boolean*string is equivalent to (boolean ?
string : "") seems past the limit. Automatic to-string when you mix a string and something else
with "+"... I'm not sure. It's reasonably obvious and awfully convenient, but is implicated in any
number of cases of "null" appearing in user interfaces.
Hans's "undef with a reason" does help a lot. I'm not sure it's enough. Because undef is
considered false for "if" purposes (a definition that seems unlikely to change) an undef that came
from a bug can lead to silently going down an execution path that doesn't happen to ever use the
value in a "concrete" way.
_______________________________________________
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
This email has been checked for viruses by AVG.
https://www.avg.com
HL
Hans L
Fri, Jul 31, 2020 4:24 AM
I've now implemented the capability for undef types to carry a reason
string.
Warnings are now emitted for basically any binary operator (and unary -)
that returns undef.
Holding a reason string does not necessarily trigger a warning unless the
higher level code explicitly checks for undef and prints the warning.
These changes (still in a Pull Request, not merged into master yet) can be
reviewed here https://github.com/openscad/openscad/pull/3383
Changing behavior of operator[] and len() are out of the scope of this pull
request and I've left them as they are.
On Thu, Jul 30, 2020 at 10:32 PM OzAtMichael oz.at.michael@gmail.com
wrote:
And len should go back to silently returning undef when applied to a
scalar.
+1
One of the really good things about OpenSCAD is its simplicity.
Beginners can get started with uncomplicated code generating straight
forward objects.
But there is also much sophistication that can be used once experienced
enough.
Adding strong checks then requires complexity, Nopheads '
if(is_list(type) && len(type) > 3 && type[3])' example shows this.
See how other languages draw a cube:
https://rosettacode.org/wiki/Draw_a_cuboid
There are plenty of fully formed languages with strong typing and all the
extra complexity (
https://en.wikipedia.org/wiki/Syntactic_sugar#Syntactic_salt)
We have been able to add more features like list comprehensions, assert,
advanced echo, which are useful for advanced programmers, but which the
beginner can ignore.
The change to len() nophead mentions was the start of forcing additional
complexity.
If you want a language which forces the programmer to write code that
enforces ridged safe programming, there are plenty of them to choose.
From: Discuss [mailto:discuss-bounces@lists.openscad.org] *On Behalf Of
*nop head
Sent: Thu, 30 Jul 2020 04:57
To: OpenSCAD general discussion
Subject: Re: [OpenSCAD] What happened to booleans?
The issue is that OpenSCAD has been used for many years and has always
returned undef for an out of bounds index so I use it as a shortcut.
Instead of if(len(type) > 3 && type[3]) is just if(type[3]), which is a
lot less ugly. It saves adding empty values to the end of my object
descriptors and makes the lack of an entry the same as an empty entry,
which makes semantic sense to me. And len should go back to silently
returning undef when applied to a scalar. Otherwise it becomes
if(is_list(type) && len(type) > 3 && type[3]), which is even more ugly.
In practice the list element is wrapped in an accessor function, so the
intent becomes obvious. E.g.
conn = camera_connector_size(type);
if(conn) {
use conn to draw the connector
}
I doubt there are many cases where people do if(list[n]) where it is a
mistake, using list[n] to compute something or compare it to anything
other than undef is definitely an error if the list isn't that long and my
proposal would catch all other errors due to bad indexing.
If an undef value goes down an execution path that doesn't use it then it
isn't a bug as far as I am concerned. undef exists as a value, can be
assigned to variables and passed as parameters and silently tested as false
with if, so I don't think that should change. But if it always stores how
it became undef then debugging is easy because if you ever use it in an
expression you will get a warning showing where it became undef and where
it was finally used. Exactly what you need to debug.
Whether operators get overloaded to work on strings in the future doesn't
matter. At the moment they don't so you get undef.
On Wed, 29 Jul 2020 at 19:09, Jordan Brown openscad@jordan.maileater.net
wrote:
[ Please forgive my tendency to write a lot about this. It's actually a
primary interest in my professional life - how to write programs to make it
difficult to have bugs, how to detect the bugs you can't prevent, and how
to design languages, libraries, and other infrastructure so as to prevent
and detect bugs. A related topic is "how to report errors so that the error
reports are useful". ]
On 7/29/2020 12:10 AM, Rogier Wolff wrote:
Some "failures" are not failures, [...]
So... when talking abstractly: "You should throw errors as close as
possible to the line causing the problem." I'm all for it.... But when you
think about it, it is not as clear cut as you might think.
Yes. Distinguishing a likely bug from an "expected" error is important.
Nop head's "when you need a concrete answer" test is certainly a good
start. But I don't think it's quite enough. The next question is "what is
a likely bug, and what is merely missing data?".
A missing parameter (in OpenSCAD, where missing parameters are expected)
is clearly not an error. (But maybe OpenSCAD should have a syntax that
says "this parameter is required".)
An attempt to divide by zero is clearly an error. In the unlikely event
that you really want something infinity-like as the result, require the
programmer to explicitly check first. (atan2() comes to mind.)
I think it's clear that if a function requires a particular parameter,
then not supplying it is an error. (But not everybody agrees. In C, I
often see people checking required parameters for NULL and returning an
error if they're NULL. No! The parameter is required; if the caller
supplied a NULL then the caller unquestionably has a bug!)
But an array index that's out of bounds? Hard to say. You might say it's
a bug, or you might say that returning undef allows the program to probe
for the dimensions of the array. I would come down on the side of calling
it an error. If you want to probe for the dimensions of the array, you
can use len() and then the intent is obvious. That's a key thing:
having the program convey the programmer's intent. If you have
out-of-bounds references simply return undef, neither the interpreter nor a
reviewer can tell what the intent is, and so you can't detect the bug. If,
on the other hand, you consider it an error, then you have two distinct
idioms: if your intent is to retrieve a value (and you don't expect your
index to be out of bounds) then you do a simple reference; if your intent
is to check for the presence of the value then you use len(). Yes, if
"undef" was a reasonable default value you have to go a bit out of your way
and say i < len(x) ? x[i] : undef, but that doesn't seem like an onerous
price to pay for protecting all of the other cases.
Mixed-type expressions are particularly tricky. There are reasonable
definitions for many combinations. I'd draw the line at the point where
you have to "stretch" to say that the definition is reasonable. String
concatenation through '+' is reasonable. String replication through
multiplication seems reasonable. Saying that boolean*string is equivalent
to (boolean ? string : "") seems past the limit. Automatic to-string
when you mix a string and something else with "+"... I'm not sure. It's
reasonably obvious and awfully convenient, but is implicated in any number
of cases of "null" appearing in user interfaces.
Hans's "undef with a reason" does help a lot. I'm not sure it's enough.
Because undef is considered false for "if" purposes (a definition that
seems unlikely to change) an undef that came from a bug can lead to
silently going down an execution path that doesn't happen to ever use the
value in a "concrete" way.
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
[image: AVG logo] https://www.avg.com/internet-security
This email has been checked for viruses by AVG antivirus software.
www.avg.com https://www.avg.com/internet-security
<#m_-8611639268707542838_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
I've now implemented the capability for undef types to carry a reason
string.
Warnings are now emitted for basically any binary operator (and unary -)
that returns undef.
Holding a reason string does not necessarily trigger a warning unless the
higher level code explicitly checks for undef and prints the warning.
These changes (still in a Pull Request, not merged into master yet) can be
reviewed here https://github.com/openscad/openscad/pull/3383
Changing behavior of operator[] and len() are out of the scope of this pull
request and I've left them as they are.
On Thu, Jul 30, 2020 at 10:32 PM OzAtMichael <oz.at.michael@gmail.com>
wrote:
> > And len should go back to silently returning undef when applied to a
> scalar.
>
>
>
> +1
>
>
>
> One of the really good things about OpenSCAD is its simplicity.
>
> Beginners can get started with uncomplicated code generating straight
> forward objects.
>
> But there is also much sophistication that can be used once experienced
> enough.
>
>
>
> Adding strong checks then requires complexity, Nopheads '
> if(is_list(type) && len(type) > 3 && type[3])' example shows this.
>
>
>
> See how other languages draw a cube:
> https://rosettacode.org/wiki/Draw_a_cuboid
>
>
>
> There are plenty of fully formed languages with strong typing and all the
> extra complexity (
> https://en.wikipedia.org/wiki/Syntactic_sugar#Syntactic_salt)
>
>
>
> We have been able to add more features like list comprehensions, assert,
> advanced echo, which are useful for advanced programmers, but which the
> beginner can ignore.
>
>
>
> The change to len() nophead mentions was the start of forcing additional
> complexity.
>
>
>
> If you want a language which forces the programmer to write code that
> enforces ridged safe programming, there are plenty of them to choose.
>
>
>
>
> ------------------------------
>
> *From:* Discuss [mailto:discuss-bounces@lists.openscad.org] *On Behalf Of
> *nop head
> *Sent:* Thu, 30 Jul 2020 04:57
> *To:* OpenSCAD general discussion
> *Subject:* Re: [OpenSCAD] What happened to booleans?
>
>
>
> The issue is that OpenSCAD has been used for many years and has always
> returned undef for an out of bounds index so I use it as a shortcut.
> Instead of if(len(type) > 3 && type[3]) is just if(type[3]), which is a
> lot less ugly. It saves adding empty values to the end of my object
> descriptors and makes the lack of an entry the same as an empty entry,
> which makes semantic sense to me. And len should go back to silently
> returning undef when applied to a scalar. Otherwise it becomes
> if(is_list(type) && len(type) > 3 && type[3]), which is even more ugly.
> In practice the list element is wrapped in an accessor function, so the
> intent becomes obvious. E.g.
>
>
>
> conn = camera_connector_size(type);
> if(conn) {
>
> use conn to draw the connector
>
> }
>
>
>
> I doubt there are many cases where people do if(list[n]) where it is a
> mistake, using list[n] to compute something or compare it to anything
> other than undef is definitely an error if the list isn't that long and my
> proposal would catch all other errors due to bad indexing.
>
>
>
> If an undef value goes down an execution path that doesn't use it then it
> isn't a bug as far as I am concerned. undef exists as a value, can be
> assigned to variables and passed as parameters and silently tested as false
> with if, so I don't think that should change. But if it always stores how
> it became undef then debugging is easy because if you ever use it in an
> expression you will get a warning showing where it became undef and where
> it was finally used. Exactly what you need to debug.
>
>
>
> Whether operators get overloaded to work on strings in the future doesn't
> matter. At the moment they don't so you get undef.
>
>
>
>
>
> On Wed, 29 Jul 2020 at 19:09, Jordan Brown <openscad@jordan.maileater.net>
> wrote:
>
> [ Please forgive my tendency to write a lot about this. It's actually a
> primary interest in my professional life - how to write programs to make it
> difficult to have bugs, how to detect the bugs you can't prevent, and how
> to design languages, libraries, and other infrastructure so as to prevent
> and detect bugs. A related topic is "how to report errors so that the error
> reports are useful". ]
>
> On 7/29/2020 12:10 AM, Rogier Wolff wrote:
>
> Some "failures" are not failures, [...]
>
> So... when talking abstractly: "You should throw errors as close as
> possible to the line causing the problem." I'm all for it.... But when you
> think about it, it is not as clear cut as you might think.
>
>
> Yes. Distinguishing a likely bug from an "expected" error is important.
>
> Nop head's "when you need a concrete answer" test is certainly a good
> start. But I don't think it's quite enough. The next question is "what is
> a likely bug, and what is merely missing data?".
>
> A missing parameter (in OpenSCAD, where missing parameters are expected)
> is clearly not an error. (But maybe OpenSCAD should have a syntax that
> says "this parameter is required".)
>
> An attempt to divide by zero is clearly an error. In the unlikely event
> that you really want something infinity-like as the result, require the
> programmer to explicitly check first. (atan2() comes to mind.)
>
> I think it's clear that if a function requires a particular parameter,
> then not supplying it is an error. (But not everybody agrees. In C, I
> often see people checking required parameters for NULL and returning an
> error if they're NULL. No! The parameter is required; if the caller
> supplied a NULL then the caller unquestionably has a bug!)
>
> But an array index that's out of bounds? Hard to say. You might say it's
> a bug, or you might say that returning undef allows the program to probe
> for the dimensions of the array. I would come down on the side of calling
> it an error. If you *want* to probe for the dimensions of the array, you
> can use len() and then the intent is obvious. That's a key thing:
> having the program convey the programmer's intent. If you have
> out-of-bounds references simply return undef, neither the interpreter nor a
> reviewer can tell what the intent is, and so you can't detect the bug. If,
> on the other hand, you consider it an error, then you have two distinct
> idioms: if your intent is to retrieve a value (and you don't expect your
> index to be out of bounds) then you do a simple reference; if your intent
> is to check for the presence of the value then you use len(). Yes, if
> "undef" was a reasonable default value you have to go a bit out of your way
> and say i < len(x) ? x[i] : undef, but that doesn't seem like an onerous
> price to pay for protecting all of the *other* cases.
>
> Mixed-type expressions are particularly tricky. There are reasonable
> definitions for many combinations. I'd draw the line at the point where
> you have to "stretch" to say that the definition is reasonable. String
> concatenation through '+' is reasonable. String replication through
> multiplication seems reasonable. Saying that boolean*string is equivalent
> to (boolean ? string : "") seems past the limit. Automatic to-string
> when you mix a string and something else with "+"... I'm not sure. It's
> reasonably obvious and awfully convenient, but is implicated in any number
> of cases of "null" appearing in user interfaces.
>
> Hans's "undef with a reason" does help a lot. I'm not sure it's enough.
> Because undef is considered false for "if" purposes (a definition that
> seems unlikely to change) an undef that came from a bug can lead to
> silently going down an execution path that doesn't happen to ever use the
> value in a "concrete" way.
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
>
>
> ------------------------------
> [image: AVG logo] <https://www.avg.com/internet-security>
>
> This email has been checked for viruses by AVG antivirus software.
> www.avg.com <https://www.avg.com/internet-security>
>
> <#m_-8611639268707542838_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
JB
Jordan Brown
Fri, Jul 31, 2020 5:57 AM
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a reason
string. Â
Cool.
So if your program has an undef in its hands, is there a straightforward
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report the
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context and
assert(false) is an error, so it's not stretching the rule very much.
On 7/30/2020 9:24 PM, Hans L wrote:
> I've now implemented the capability for undef types to carry a reason
> string. Â
Cool.
So if your program has an undef in its hands, is there a straightforward
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to _retrieve_ the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report the
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context and
assert(false) is an error, so it's not stretching the rule very much.
A
adrianv
Fri, Jul 31, 2020 10:36 AM
Another question: is there a way to create an undef and associate a reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a reason
string. Â
Cool.
So if your program has an undef in its hands, is there a straightforward
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report the
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context and
assert(false) is an error, so it's not stretching the rule very much.
OpenSCAD mailing list
Another question: is there a way to create an undef and associate a reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
> On 7/30/2020 9:24 PM, Hans L wrote:
>> I've now implemented the capability for undef types to carry a reason
>> string. Â
>
> Cool.
>
> So if your program has an undef in its hands, is there a straightforward
> way to say "what is the reason for this undef"?
>
> I don't know whether it's necessary to have a way to _retrieve_ the
> reason, or just a way to say "hey, I don't want an undef, this is an
> error, report the error with the reason for the undef".
>
> One really simple mechanism might be to have assert(undef) report the
> reason for the undef.
>
> That would be an exception to the general rule that undef is OK in a
> boolean context, but then again undef is false in a boolean context and
> assert(false) is an error, so it's not stretching the rule very much.
>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@.openscad
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
Sent from: http://forum.openscad.org/
HL
Hans L
Fri, Jul 31, 2020 7:19 PM
No I haven't added any mechanism to query or set undefined strings
programmatically from scripts, since I felt like that needed some more
thought and planning around the interface, and wanted to limit this PR to
only the least contentious changes.
Adding either of those should be fairly quick and simple to implement
though.
For displaying the string, i considered that echo() and str() could
possibly automatically show the undef strings if any. But this might end
up with more verbose messages than people would like for general cases.
Also as far as I know, echo() and str() generally work in such a way where
you could copy/paste output back into a script and it should be parsed into
the same literal values. So doing something like that should
probably require that we first lock in how the string would be set
programmatically (see below).
Having assert() handle undef as a special case might be one good way to
inspect for reason strings though.
Another possibility is that the reason string could be a "member lookup" on
undef types, so e.g. ( some_undef_value.reason ) could return a string.
But then should an undef without any string return undef for that as
well?(its undefs all the way down!)
And would ".reason" on a non-undef type also be undef, or throw an actual
error?
For setting the string, I don't think calling undef() like a function is a
good idea, since we don't want to mix up the default literal "undef"
identifier with a new builtin function(constructor basically) identifier of
the same name. But perhaps a function named undefined() would be fine, for
example.
On Fri, Jul 31, 2020 at 5:36 AM adrianv avm4@cornell.edu wrote:
Another question: is there a way to create an undef and associate a
reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a reason
string.
Cool.
So if your program has an undef in its hands, is there a straightforward
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report the
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context and
assert(false) is an error, so it's not stretching the rule very much.
OpenSCAD mailing list
No I haven't added any mechanism to query or set undefined strings
programmatically from scripts, since I felt like that needed some more
thought and planning around the interface, and wanted to limit this PR to
only the least contentious changes.
Adding either of those should be fairly quick and simple to implement
though.
For displaying the string, i considered that echo() and str() could
possibly automatically show the undef strings if any. But this might end
up with more verbose messages than people would like for general cases.
Also as far as I know, echo() and str() generally work in such a way where
you could copy/paste output back into a script and it should be parsed into
the same literal values. So doing something like that should
probably require that we first lock in how the string would be set
programmatically (see below).
Having assert() handle undef as a special case might be one good way to
inspect for reason strings though.
Another possibility is that the reason string could be a "member lookup" on
undef types, so e.g. ( some_undef_value.reason ) could return a string.
But then should an undef without any string return undef for that as
well?(its undefs all the way down!)
And would ".reason" on a non-undef type also be undef, or throw an actual
error?
For setting the string, I don't think calling undef() like a function is a
good idea, since we don't want to mix up the default literal "undef"
identifier with a new builtin function(constructor basically) identifier of
the same name. But perhaps a function named undefined() would be fine, for
example.
On Fri, Jul 31, 2020 at 5:36 AM adrianv <avm4@cornell.edu> wrote:
> Another question: is there a way to create an undef and associate a
> reason,
> so for example
>
> undef("line_intersection: lines did not intersect")
>
>
>
> JordanBrown wrote
> > On 7/30/2020 9:24 PM, Hans L wrote:
> >> I've now implemented the capability for undef types to carry a reason
> >> string.
> >
> > Cool.
> >
> > So if your program has an undef in its hands, is there a straightforward
> > way to say "what is the reason for this undef"?
> >
> > I don't know whether it's necessary to have a way to _retrieve_ the
> > reason, or just a way to say "hey, I don't want an undef, this is an
> > error, report the error with the reason for the undef".
> >
> > One really simple mechanism might be to have assert(undef) report the
> > reason for the undef.
> >
> > That would be an exception to the general rule that undef is OK in a
> > boolean context, but then again undef is false in a boolean context and
> > assert(false) is an error, so it's not stretching the rule very much.
> >
> >
> > _______________________________________________
> > 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
>
JB
Jordan Brown
Fri, Jul 31, 2020 9:29 PM
On 7/31/2020 12:19 PM, Hans L wrote:
For setting the string, I don't think calling undef() like a function
is a good idea, since we don't want to mix up the default literal
"undef" identifier with a new builtin function(constructor basically)
identifier of the same name.
It's a coin flip. Having the two with the same name would be in a sense
confusing, but at the same time it makes the association between them be
really strong... and it should be really strong.
Of course, you can only ask this question in a language where variable
names[*] and functions are in different namespaces. But OpenSCAD is
such a language.
[*]Â Yeah, yeah, they aren't variables.
On 7/31/2020 12:19 PM, Hans L wrote:
> For setting the string, I don't think calling undef() like a function
> is a good idea, since we don't want to mix up the default literal
> "undef" identifier with a new builtin function(constructor basically)
> identifier of the same name.
It's a coin flip. Having the two with the same name would be in a sense
confusing, but at the same time it makes the association between them be
really strong... and it *should* be really strong.
Of course, you can only ask this question in a language where variable
names[*] and functions are in different namespaces. But OpenSCAD is
such a language.
[*]Â Yeah, yeah, they aren't variables.
NH
nop head
Fri, Jul 31, 2020 9:43 PM
Why does it need a different name? One of things I like about OpenSCAD is
modules, functions and variables all have their own namespace and it isn't
ambiguous which one is referred to.
On Fri, 31 Jul 2020 at 20:20, Hans L thehans@gmail.com wrote:
No I haven't added any mechanism to query or set undefined strings
programmatically from scripts, since I felt like that needed some more
thought and planning around the interface, and wanted to limit this PR to
only the least contentious changes.
Adding either of those should be fairly quick and simple to implement
though.
For displaying the string, i considered that echo() and str() could
possibly automatically show the undef strings if any. But this might end
up with more verbose messages than people would like for general cases.
Also as far as I know, echo() and str() generally work in such a way where
you could copy/paste output back into a script and it should be parsed into
the same literal values. So doing something like that should
probably require that we first lock in how the string would be set
programmatically (see below).
Having assert() handle undef as a special case might be one good way to
inspect for reason strings though.
Another possibility is that the reason string could be a "member lookup"
on undef types, so e.g. ( some_undef_value.reason ) could return a string.
But then should an undef without any string return undef for that as
well?(its undefs all the way down!)
And would ".reason" on a non-undef type also be undef, or throw an actual
error?
For setting the string, I don't think calling undef() like a function is a
good idea, since we don't want to mix up the default literal "undef"
identifier with a new builtin function(constructor basically) identifier of
the same name. But perhaps a function named undefined() would be fine, for
example.
On Fri, Jul 31, 2020 at 5:36 AM adrianv avm4@cornell.edu wrote:
Another question: is there a way to create an undef and associate a
reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a reason
string.
Cool.
So if your program has an undef in its hands, is there a straightforward
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report the
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context and
assert(false) is an error, so it's not stretching the rule very much.
OpenSCAD mailing list
Why does it need a different name? One of things I like about OpenSCAD is
modules, functions and variables all have their own namespace and it isn't
ambiguous which one is referred to.
On Fri, 31 Jul 2020 at 20:20, Hans L <thehans@gmail.com> wrote:
> No I haven't added any mechanism to query or set undefined strings
> programmatically from scripts, since I felt like that needed some more
> thought and planning around the interface, and wanted to limit this PR to
> only the least contentious changes.
>
> Adding either of those should be fairly quick and simple to implement
> though.
>
> For displaying the string, i considered that echo() and str() could
> possibly automatically show the undef strings if any. But this might end
> up with more verbose messages than people would like for general cases.
> Also as far as I know, echo() and str() generally work in such a way where
> you could copy/paste output back into a script and it should be parsed into
> the same literal values. So doing something like that should
> probably require that we first lock in how the string would be set
> programmatically (see below).
> Having assert() handle undef as a special case might be one good way to
> inspect for reason strings though.
>
> Another possibility is that the reason string could be a "member lookup"
> on undef types, so e.g. ( some_undef_value.reason ) could return a string.
> But then should an undef without any string return undef for that as
> well?(its undefs all the way down!)
> And would ".reason" on a non-undef type also be undef, or throw an actual
> error?
>
> For setting the string, I don't think calling undef() like a function is a
> good idea, since we don't want to mix up the default literal "undef"
> identifier with a new builtin function(constructor basically) identifier of
> the same name. But perhaps a function named undefined() would be fine, for
> example.
>
>
>
>
> On Fri, Jul 31, 2020 at 5:36 AM adrianv <avm4@cornell.edu> wrote:
>
>> Another question: is there a way to create an undef and associate a
>> reason,
>> so for example
>>
>> undef("line_intersection: lines did not intersect")
>>
>>
>>
>> JordanBrown wrote
>> > On 7/30/2020 9:24 PM, Hans L wrote:
>> >> I've now implemented the capability for undef types to carry a reason
>> >> string.
>> >
>> > Cool.
>> >
>> > So if your program has an undef in its hands, is there a straightforward
>> > way to say "what is the reason for this undef"?
>> >
>> > I don't know whether it's necessary to have a way to _retrieve_ the
>> > reason, or just a way to say "hey, I don't want an undef, this is an
>> > error, report the error with the reason for the undef".
>> >
>> > One really simple mechanism might be to have assert(undef) report the
>> > reason for the undef.
>> >
>> > That would be an exception to the general rule that undef is OK in a
>> > boolean context, but then again undef is false in a boolean context and
>> > assert(false) is an error, so it's not stretching the rule very much.
>> >
>> >
>> > _______________________________________________
>> > 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
>>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
DM
Doug Moen
Fri, Jul 31, 2020 10:19 PM
A different name for the constant and the function is useful in the context of the new feature under development for function values, wherein functions can exist in the variable namespace.
On Fri, Jul 31, 2020, at 9:43 PM, nop head wrote:
Why does it need a different name? One of things I like about OpenSCAD is modules, functions and variables all have their own namespace and it isn't ambiguous which one is referred to.
On Fri, 31 Jul 2020 at 20:20, Hans L thehans@gmail.com wrote:
No I haven't added any mechanism to query or set undefined strings programmatically from scripts, since I felt like that needed some more thought and planning around the interface, and wanted to limit this PR to only the least contentious changes.
Adding either of those should be fairly quick and simple to implement though.
For displaying the string, i considered that echo() and str() could possibly automatically show the undef strings if any. But this might end up with more verbose messages than people would like for general cases.
Also as far as I know, echo() and str() generally work in such a way where you could copy/paste output back into a script and it should be parsed into the same literal values. So doing something like that should probably require that we first lock in how the string would be set programmatically (see below).
Having assert() handle undef as a special case might be one good way to inspect for reason strings though.
Another possibility is that the reason string could be a "member lookup" on undef types, so e.g. ( some_undef_value.reason ) could return a string. But then should an undef without any string return undef for that as well?(its undefs all the way down!)
And would ".reason" on a non-undef type also be undef, or throw an actual error?
For setting the string, I don't think calling undef() like a function is a good idea, since we don't want to mix up the default literal "undef" identifier with a new builtin function(constructor basically) identifier of the same name. But perhaps a function named undefined() would be fine, for example.
On Fri, Jul 31, 2020 at 5:36 AM adrianv avm4@cornell.edu wrote:
Another question: is there a way to create an undef and associate a reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a reason
string.
Cool.
So if your program has an undef in its hands, is there a straightforward
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report the
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context and
assert(false) is an error, so it's not stretching the rule very much.
OpenSCAD mailing list
A different name for the constant and the function is useful in the context of the new feature under development for function values, wherein functions can exist in the variable namespace.
On Fri, Jul 31, 2020, at 9:43 PM, nop head wrote:
> Why does it need a different name? One of things I like about OpenSCAD is modules, functions and variables all have their own namespace and it isn't ambiguous which one is referred to.
>
>
> On Fri, 31 Jul 2020 at 20:20, Hans L <thehans@gmail.com> wrote:
>> No I haven't added any mechanism to query or set undefined strings programmatically from scripts, since I felt like that needed some more thought and planning around the interface, and wanted to limit this PR to only the least contentious changes.
>>
>> Adding either of those should be fairly quick and simple to implement though.
>>
>> For displaying the string, i considered that echo() and str() could possibly automatically show the undef strings if any. But this might end up with more verbose messages than people would like for general cases.
>> Also as far as I know, echo() and str() generally work in such a way where you could copy/paste output back into a script and it should be parsed into the same literal values. So doing something like that should probably require that we first lock in how the string would be set programmatically (see below).
>> Having assert() handle undef as a special case might be one good way to inspect for reason strings though.
>>
>> Another possibility is that the reason string could be a "member lookup" on undef types, so e.g. ( some_undef_value.reason ) could return a string. But then should an undef without any string return undef for that as well?(its undefs all the way down!)
>> And would ".reason" on a non-undef type also be undef, or throw an actual error?
>>
>> For setting the string, I don't think calling undef() like a function is a good idea, since we don't want to mix up the default literal "undef" identifier with a new builtin function(constructor basically) identifier of the same name. But perhaps a function named undefined() would be fine, for example.
>>
>>
>>
>>
>> On Fri, Jul 31, 2020 at 5:36 AM adrianv <avm4@cornell.edu> wrote:
>>> Another question: is there a way to create an undef and associate a reason,
>>> so for example
>>>
>>> undef("line_intersection: lines did not intersect")
>>>
>>>
>>>
>>> JordanBrown wrote
>>> > On 7/30/2020 9:24 PM, Hans L wrote:
>>> >> I've now implemented the capability for undef types to carry a reason
>>> >> string.
>>> >
>>> > Cool.
>>> >
>>> > So if your program has an undef in its hands, is there a straightforward
>>> > way to say "what is the reason for this undef"?
>>> >
>>> > I don't know whether it's necessary to have a way to _retrieve_ the
>>> > reason, or just a way to say "hey, I don't want an undef, this is an
>>> > error, report the error with the reason for the undef".
>>> >
>>> > One really simple mechanism might be to have assert(undef) report the
>>> > reason for the undef.
>>> >
>>> > That would be an exception to the general rule that undef is OK in a
>>> > boolean context, but then again undef is false in a boolean context and
>>> > assert(false) is an error, so it's not stretching the rule very much.
>>> >
>>> >
>>> > _______________________________________________
>>> > 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
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Fri, Jul 31, 2020 10:28 PM
What is confusing about calling undef() like a function? You say we don't
want to "mix up" the undef literal with an undef constructor, but what's the
difference really between
undef
and
undef("some reason text")
Isn't the first one basically the same as the second one only with the
argument missing, in some sense? What's the problem or consequence of
"mixing up" these two things? When there's no string associated with an
undef then what is there? Empty string? Another undef? It's a bit
unclear what the advantage is of not having a string. Shouldn't there
always be one? (undef: "list index out of bounds", undef: "variable not
set", undef: "mismatched argument types for +".) It seems like you'd want
to encourage users to supply the string any time they make an assignment.
I guess there's the question of what it means if you just write "foo=undef",
as in that case there's no string. The string could be "direct assignment"
or something like that.
To me it seems like doing
foo = undef
but
foo = undefined("some reason")
is more confusing because now I have to remember when to use undef and when
to use undefined. It looks like undef is already illegal as a function
name, so it wouldn't break any code, whereas undefined() is currently a
valid function, so using that could break code.
thehans wrote
No I haven't added any mechanism to query or set undefined strings
programmatically from scripts, since I felt like that needed some more
thought and planning around the interface, and wanted to limit this PR to
only the least contentious changes.
Adding either of those should be fairly quick and simple to implement
though.
For displaying the string, i considered that echo() and str() could
possibly automatically show the undef strings if any. But this might end
up with more verbose messages than people would like for general cases.
Also as far as I know, echo() and str() generally work in such a way where
you could copy/paste output back into a script and it should be parsed
into
the same literal values. So doing something like that should
probably require that we first lock in how the string would be set
programmatically (see below).
Having assert() handle undef as a special case might be one good way to
inspect for reason strings though.
Another possibility is that the reason string could be a "member lookup"
on
undef types, so e.g. ( some_undef_value.reason ) could return a string.
But then should an undef without any string return undef for that as
well?(its undefs all the way down!)
And would ".reason" on a non-undef type also be undef, or throw an actual
error?
For setting the string, I don't think calling undef() like a function is a
good idea, since we don't want to mix up the default literal "undef"
identifier with a new builtin function(constructor basically) identifier
of
the same name. But perhaps a function named undefined() would be fine,
for
example.
On Fri, Jul 31, 2020 at 5:36 AM adrianv <
Another question: is there a way to create an undef and associate a
reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a reason
string.
Cool.
So if your program has an undef in its hands, is there a
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report the
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context and
assert(false) is an error, so it's not stretching the rule very much.
OpenSCAD mailing list
What is confusing about calling undef() like a function? You say we don't
want to "mix up" the undef literal with an undef constructor, but what's the
difference really between
undef
and
undef("some reason text")
Isn't the first one basically the same as the second one only with the
argument missing, in some sense? What's the problem or consequence of
"mixing up" these two things? When there's no string associated with an
undef then what is there? Empty string? Another undef? It's a bit
unclear what the advantage is of not having a string. Shouldn't there
always be one? (undef: "list index out of bounds", undef: "variable not
set", undef: "mismatched argument types for +".) It seems like you'd want
to encourage users to supply the string any time they make an assignment.
I guess there's the question of what it means if you just write "foo=undef",
as in that case there's no string. The string could be "direct assignment"
or something like that.
To me it seems like doing
foo = undef
but
foo = undefined("some reason")
is more confusing because now I have to remember when to use undef and when
to use undefined. It looks like undef is already illegal as a function
name, so it wouldn't break any code, whereas undefined() is currently a
valid function, so using that could break code.
thehans wrote
> No I haven't added any mechanism to query or set undefined strings
> programmatically from scripts, since I felt like that needed some more
> thought and planning around the interface, and wanted to limit this PR to
> only the least contentious changes.
>
> Adding either of those should be fairly quick and simple to implement
> though.
>
> For displaying the string, i considered that echo() and str() could
> possibly automatically show the undef strings if any. But this might end
> up with more verbose messages than people would like for general cases.
> Also as far as I know, echo() and str() generally work in such a way where
> you could copy/paste output back into a script and it should be parsed
> into
> the same literal values. So doing something like that should
> probably require that we first lock in how the string would be set
> programmatically (see below).
> Having assert() handle undef as a special case might be one good way to
> inspect for reason strings though.
>
> Another possibility is that the reason string could be a "member lookup"
> on
> undef types, so e.g. ( some_undef_value.reason ) could return a string.
> But then should an undef without any string return undef for that as
> well?(its undefs all the way down!)
> And would ".reason" on a non-undef type also be undef, or throw an actual
> error?
>
> For setting the string, I don't think calling undef() like a function is a
> good idea, since we don't want to mix up the default literal "undef"
> identifier with a new builtin function(constructor basically) identifier
> of
> the same name. But perhaps a function named undefined() would be fine,
> for
> example.
>
>
>
>
> On Fri, Jul 31, 2020 at 5:36 AM adrianv <
> avm4@
> > wrote:
>
>> Another question: is there a way to create an undef and associate a
>> reason,
>> so for example
>>
>> undef("line_intersection: lines did not intersect")
>>
>>
>>
>> JordanBrown wrote
>> > On 7/30/2020 9:24 PM, Hans L wrote:
>> >> I've now implemented the capability for undef types to carry a reason
>> >> string.
>> >
>> > Cool.
>> >
>> > So if your program has an undef in its hands, is there a
>> straightforward
>> > way to say "what is the reason for this undef"?
>> >
>> > I don't know whether it's necessary to have a way to _retrieve_ the
>> > reason, or just a way to say "hey, I don't want an undef, this is an
>> > error, report the error with the reason for the undef".
>> >
>> > One really simple mechanism might be to have assert(undef) report the
>> > reason for the undef.
>> >
>> > That would be an exception to the general rule that undef is OK in a
>> > boolean context, but then again undef is false in a boolean context and
>> > assert(false) is an error, so it's not stretching the rule very much.
>> >
>> >
>> > _______________________________________________
>> > 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@.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/
HL
Hans L
Fri, Jul 31, 2020 10:44 PM
As Doug points out, the situation is more complicated if you account for
function literals, which already exist as an experimental feature.
It's not necessarily an issue at the moment, but If we ever decided to make
builtin functions accessible as literals (personally i would like to see
this), then writing: foo = undef; would be ambiguous whether you want the
function literal value or the actual undef literal.
On Fri, Jul 31, 2020 at 5:29 PM adrianv avm4@cornell.edu wrote:
What is confusing about calling undef() like a function? You say we don't
want to "mix up" the undef literal with an undef constructor, but what's
the
difference really between
undef
and
undef("some reason text")
Isn't the first one basically the same as the second one only with the
argument missing, in some sense? What's the problem or consequence of
"mixing up" these two things? When there's no string associated with an
undef then what is there? Empty string? Another undef? It's a bit
unclear what the advantage is of not having a string. Shouldn't there
always be one? (undef: "list index out of bounds", undef: "variable not
set", undef: "mismatched argument types for +".) It seems like you'd want
to encourage users to supply the string any time they make an assignment.
I guess there's the question of what it means if you just write
"foo=undef",
as in that case there's no string. The string could be "direct assignment"
or something like that.
To me it seems like doing
foo = undef
but
foo = undefined("some reason")
is more confusing because now I have to remember when to use undef and when
to use undefined. It looks like undef is already illegal as a function
name, so it wouldn't break any code, whereas undefined() is currently a
valid function, so using that could break code.
thehans wrote
No I haven't added any mechanism to query or set undefined strings
programmatically from scripts, since I felt like that needed some more
thought and planning around the interface, and wanted to limit this PR to
only the least contentious changes.
Adding either of those should be fairly quick and simple to implement
though.
For displaying the string, i considered that echo() and str() could
possibly automatically show the undef strings if any. But this might end
up with more verbose messages than people would like for general cases.
Also as far as I know, echo() and str() generally work in such a way
you could copy/paste output back into a script and it should be parsed
into
the same literal values. So doing something like that should
probably require that we first lock in how the string would be set
programmatically (see below).
Having assert() handle undef as a special case might be one good way to
inspect for reason strings though.
Another possibility is that the reason string could be a "member lookup"
on
undef types, so e.g. ( some_undef_value.reason ) could return a string.
But then should an undef without any string return undef for that as
well?(its undefs all the way down!)
And would ".reason" on a non-undef type also be undef, or throw an actual
error?
For setting the string, I don't think calling undef() like a function is
good idea, since we don't want to mix up the default literal "undef"
identifier with a new builtin function(constructor basically) identifier
of
the same name. But perhaps a function named undefined() would be fine,
for
example.
On Fri, Jul 31, 2020 at 5:36 AM adrianv <
Another question: is there a way to create an undef and associate a
reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a reason
string.
Cool.
So if your program has an undef in its hands, is there a
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report the
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context
assert(false) is an error, so it's not stretching the rule very much.
OpenSCAD mailing list
As Doug points out, the situation is more complicated if you account for
function literals, which already exist as an experimental feature.
It's not necessarily an issue at the moment, but If we ever decided to make
builtin functions accessible as literals (personally i would like to see
this), then writing: foo = undef; would be ambiguous whether you want the
function literal value or the actual undef literal.
On Fri, Jul 31, 2020 at 5:29 PM adrianv <avm4@cornell.edu> wrote:
> What is confusing about calling undef() like a function? You say we don't
> want to "mix up" the undef literal with an undef constructor, but what's
> the
> difference really between
>
> undef
>
> and
>
> undef("some reason text")
>
> Isn't the first one basically the same as the second one only with the
> argument missing, in some sense? What's the problem or consequence of
> "mixing up" these two things? When there's no string associated with an
> undef then what is there? Empty string? Another undef? It's a bit
> unclear what the advantage is of not having a string. Shouldn't there
> always be one? (undef: "list index out of bounds", undef: "variable not
> set", undef: "mismatched argument types for +".) It seems like you'd want
> to encourage users to supply the string any time they make an assignment.
>
> I guess there's the question of what it means if you just write
> "foo=undef",
> as in that case there's no string. The string could be "direct assignment"
> or something like that.
>
> To me it seems like doing
>
> foo = undef
>
> but
>
> foo = undefined("some reason")
>
> is more confusing because now I have to remember when to use undef and when
> to use undefined. It looks like undef is already illegal as a function
> name, so it wouldn't break any code, whereas undefined() is currently a
> valid function, so using that could break code.
>
>
>
> thehans wrote
> > No I haven't added any mechanism to query or set undefined strings
> > programmatically from scripts, since I felt like that needed some more
> > thought and planning around the interface, and wanted to limit this PR to
> > only the least contentious changes.
> >
> > Adding either of those should be fairly quick and simple to implement
> > though.
> >
> > For displaying the string, i considered that echo() and str() could
> > possibly automatically show the undef strings if any. But this might end
> > up with more verbose messages than people would like for general cases.
> > Also as far as I know, echo() and str() generally work in such a way
> where
> > you could copy/paste output back into a script and it should be parsed
> > into
> > the same literal values. So doing something like that should
> > probably require that we first lock in how the string would be set
> > programmatically (see below).
> > Having assert() handle undef as a special case might be one good way to
> > inspect for reason strings though.
> >
> > Another possibility is that the reason string could be a "member lookup"
> > on
> > undef types, so e.g. ( some_undef_value.reason ) could return a string.
> > But then should an undef without any string return undef for that as
> > well?(its undefs all the way down!)
> > And would ".reason" on a non-undef type also be undef, or throw an actual
> > error?
> >
> > For setting the string, I don't think calling undef() like a function is
> a
> > good idea, since we don't want to mix up the default literal "undef"
> > identifier with a new builtin function(constructor basically) identifier
> > of
> > the same name. But perhaps a function named undefined() would be fine,
> > for
> > example.
> >
> >
> >
> >
> > On Fri, Jul 31, 2020 at 5:36 AM adrianv <
>
> > avm4@
>
> > > wrote:
> >
> >> Another question: is there a way to create an undef and associate a
> >> reason,
> >> so for example
> >>
> >> undef("line_intersection: lines did not intersect")
> >>
> >>
> >>
> >> JordanBrown wrote
> >> > On 7/30/2020 9:24 PM, Hans L wrote:
> >> >> I've now implemented the capability for undef types to carry a reason
> >> >> string.
> >> >
> >> > Cool.
> >> >
> >> > So if your program has an undef in its hands, is there a
> >> straightforward
> >> > way to say "what is the reason for this undef"?
> >> >
> >> > I don't know whether it's necessary to have a way to _retrieve_ the
> >> > reason, or just a way to say "hey, I don't want an undef, this is an
> >> > error, report the error with the reason for the undef".
> >> >
> >> > One really simple mechanism might be to have assert(undef) report the
> >> > reason for the undef.
> >> >
> >> > That would be an exception to the general rule that undef is OK in a
> >> > boolean context, but then again undef is false in a boolean context
> and
> >> > assert(false) is an error, so it's not stretching the rule very much.
> >> >
> >> >
> >> > _______________________________________________
> >> > 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@.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/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Fri, Jul 31, 2020 11:34 PM
I hadn't really thought about how function literals might be involved here.
But I'm a little puzzled. Suppose builtin functions can be treated as
literals as shown below. Then
foo = sin
is ambiguous about whether I mean the function "sin" or the variable "sin".
I would think that if you want to make regular functions (and built-ins)
available as literals---which is a really good idea because otherwise it
creates a confusing situation where some functions need to be redefined as
function literals for convenience---you should do something like what MATLAB
does and create a syntax for it. In MATLAB you create a function literal
(they call them handles) from (any) function using "@". So in MATLAB
foo = @sin
assigns a function literal into foo. And the call
foo(@sin,@cos)
passes two function literals into the function foo. Given how namespaces
work in OpenSCAD there's no way to assign a pre-existing function to a
variable or pass functions as literals without a new syntax that "converts"
the function into a literal.
But my point is that
foo=undef
is not any more ambiguous than
foo=sin
so it's not a problem that has to do with undef. So I still don't see any
issue with using the functional form I proposed.
foo = undef; // Plain undef without text
foo = undef("reason"); // undef with text
foo = @undef; // or whatever new syntax.... the undef function...though
why would you ever want that?
Have I missed something?
thehans wrote
As Doug points out, the situation is more complicated if you account for
function literals, which already exist as an experimental feature.
It's not necessarily an issue at the moment, but If we ever decided to
make
builtin functions accessible as literals (personally i would like to see
this), then writing: foo = undef; would be ambiguous whether you want the
function literal value or the actual undef literal.
On Fri, Jul 31, 2020 at 5:29 PM adrianv <
What is confusing about calling undef() like a function? You say we
don't
want to "mix up" the undef literal with an undef constructor, but what's
the
difference really between
undef
and
undef("some reason text")
Isn't the first one basically the same as the second one only with the
argument missing, in some sense? What's the problem or consequence of
"mixing up" these two things? When there's no string associated with an
undef then what is there? Empty string? Another undef? It's a bit
unclear what the advantage is of not having a string. Shouldn't there
always be one? (undef: "list index out of bounds", undef: "variable not
set", undef: "mismatched argument types for +".) It seems like you'd
want
to encourage users to supply the string any time they make an assignment.
I guess there's the question of what it means if you just write
"foo=undef",
as in that case there's no string. The string could be "direct
assignment"
or something like that.
To me it seems like doing
foo = undef
but
foo = undefined("some reason")
is more confusing because now I have to remember when to use undef and
when
to use undefined. It looks like undef is already illegal as a function
name, so it wouldn't break any code, whereas undefined() is currently a
valid function, so using that could break code.
thehans wrote
No I haven't added any mechanism to query or set undefined strings
programmatically from scripts, since I felt like that needed some more
thought and planning around the interface, and wanted to limit this PR
only the least contentious changes.
Adding either of those should be fairly quick and simple to implement
though.
For displaying the string, i considered that echo() and str() could
possibly automatically show the undef strings if any. But this might
up with more verbose messages than people would like for general cases.
Also as far as I know, echo() and str() generally work in such a way
you could copy/paste output back into a script and it should be parsed
into
the same literal values. So doing something like that should
probably require that we first lock in how the string would be set
programmatically (see below).
Having assert() handle undef as a special case might be one good way to
inspect for reason strings though.
Another possibility is that the reason string could be a "member
on
undef types, so e.g. ( some_undef_value.reason ) could return a string.
But then should an undef without any string return undef for that as
well?(its undefs all the way down!)
And would ".reason" on a non-undef type also be undef, or throw an
error?
For setting the string, I don't think calling undef() like a function
good idea, since we don't want to mix up the default literal "undef"
identifier with a new builtin function(constructor basically)
of
the same name. But perhaps a function named undefined() would be fine,
for
example.
On Fri, Jul 31, 2020 at 5:36 AM adrianv <
Another question: is there a way to create an undef and associate a
reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a
Cool.
So if your program has an undef in its hands, is there a
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is an
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report
reason for the undef.
That would be an exception to the general rule that undef is OK in a
boolean context, but then again undef is false in a boolean context
assert(false) is an error, so it's not stretching the rule very
I hadn't really thought about how function literals might be involved here.
But I'm a little puzzled. Suppose builtin functions can be treated as
literals as shown below. Then
foo = sin
is ambiguous about whether I mean the function "sin" or the variable "sin".
I would think that if you want to make regular functions (and built-ins)
available as literals---which is a really good idea because otherwise it
creates a confusing situation where some functions need to be redefined as
function literals for convenience---you should do something like what MATLAB
does and create a syntax for it. In MATLAB you create a function literal
(they call them handles) from (any) function using "@". So in MATLAB
foo = @sin
assigns a function literal into foo. And the call
foo(@sin,@cos)
passes two function literals into the function foo. Given how namespaces
work in OpenSCAD there's no way to assign a pre-existing function to a
variable or pass functions as literals without a new syntax that "converts"
the function into a literal.
But my point is that
foo=undef
is not any more ambiguous than
foo=sin
so it's not a problem that has to do with undef. So I still don't see any
issue with using the functional form I proposed.
foo = undef; // Plain undef without text
foo = undef("reason"); // undef with text
foo = @undef; // or whatever new syntax.... the undef function...though
why would you ever want that?
Have I missed something?
thehans wrote
> As Doug points out, the situation is more complicated if you account for
> function literals, which already exist as an experimental feature.
> It's not necessarily an issue at the moment, but If we ever decided to
> make
> builtin functions accessible as literals (personally i would like to see
> this), then writing: foo = undef; would be ambiguous whether you want the
> function literal value or the actual undef literal.
>
> On Fri, Jul 31, 2020 at 5:29 PM adrianv <
> avm4@
> > wrote:
>
>> What is confusing about calling undef() like a function? You say we
>> don't
>> want to "mix up" the undef literal with an undef constructor, but what's
>> the
>> difference really between
>>
>> undef
>>
>> and
>>
>> undef("some reason text")
>>
>> Isn't the first one basically the same as the second one only with the
>> argument missing, in some sense? What's the problem or consequence of
>> "mixing up" these two things? When there's no string associated with an
>> undef then what is there? Empty string? Another undef? It's a bit
>> unclear what the advantage is of not having a string. Shouldn't there
>> always be one? (undef: "list index out of bounds", undef: "variable not
>> set", undef: "mismatched argument types for +".) It seems like you'd
>> want
>> to encourage users to supply the string any time they make an assignment.
>>
>> I guess there's the question of what it means if you just write
>> "foo=undef",
>> as in that case there's no string. The string could be "direct
>> assignment"
>> or something like that.
>>
>> To me it seems like doing
>>
>> foo = undef
>>
>> but
>>
>> foo = undefined("some reason")
>>
>> is more confusing because now I have to remember when to use undef and
>> when
>> to use undefined. It looks like undef is already illegal as a function
>> name, so it wouldn't break any code, whereas undefined() is currently a
>> valid function, so using that could break code.
>>
>>
>>
>> thehans wrote
>> > No I haven't added any mechanism to query or set undefined strings
>> > programmatically from scripts, since I felt like that needed some more
>> > thought and planning around the interface, and wanted to limit this PR
>> to
>> > only the least contentious changes.
>> >
>> > Adding either of those should be fairly quick and simple to implement
>> > though.
>> >
>> > For displaying the string, i considered that echo() and str() could
>> > possibly automatically show the undef strings if any. But this might
>> end
>> > up with more verbose messages than people would like for general cases.
>> > Also as far as I know, echo() and str() generally work in such a way
>> where
>> > you could copy/paste output back into a script and it should be parsed
>> > into
>> > the same literal values. So doing something like that should
>> > probably require that we first lock in how the string would be set
>> > programmatically (see below).
>> > Having assert() handle undef as a special case might be one good way to
>> > inspect for reason strings though.
>> >
>> > Another possibility is that the reason string could be a "member
>> lookup"
>> > on
>> > undef types, so e.g. ( some_undef_value.reason ) could return a string.
>> > But then should an undef without any string return undef for that as
>> > well?(its undefs all the way down!)
>> > And would ".reason" on a non-undef type also be undef, or throw an
>> actual
>> > error?
>> >
>> > For setting the string, I don't think calling undef() like a function
>> is
>> a
>> > good idea, since we don't want to mix up the default literal "undef"
>> > identifier with a new builtin function(constructor basically)
>> identifier
>> > of
>> > the same name. But perhaps a function named undefined() would be fine,
>> > for
>> > example.
>> >
>> >
>> >
>> >
>> > On Fri, Jul 31, 2020 at 5:36 AM adrianv <
>>
>> > avm4@
>>
>> > > wrote:
>> >
>> >> Another question: is there a way to create an undef and associate a
>> >> reason,
>> >> so for example
>> >>
>> >> undef("line_intersection: lines did not intersect")
>> >>
>> >>
>> >>
>> >> JordanBrown wrote
>> >> > On 7/30/2020 9:24 PM, Hans L wrote:
>> >> >> I've now implemented the capability for undef types to carry a
>> reason
>> >> >> string.
>> >> >
>> >> > Cool.
>> >> >
>> >> > So if your program has an undef in its hands, is there a
>> >> straightforward
>> >> > way to say "what is the reason for this undef"?
>> >> >
>> >> > I don't know whether it's necessary to have a way to _retrieve_ the
>> >> > reason, or just a way to say "hey, I don't want an undef, this is an
>> >> > error, report the error with the reason for the undef".
>> >> >
>> >> > One really simple mechanism might be to have assert(undef) report
>> the
>> >> > reason for the undef.
>> >> >
>> >> > That would be an exception to the general rule that undef is OK in a
>> >> > boolean context, but then again undef is false in a boolean context
>> and
>> >> > assert(false) is an error, so it's not stretching the rule very
>> much.
>> >> >
>> >> >
>> >> > _______________________________________________
>> >> > 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@.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/
>>
>> _______________________________________________
>> 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/
HL
Hans L
Sat, Aug 1, 2020 12:40 AM
The difference is that undef is an already established builtin constant
value, but sin is not.
x = sin;
WARNING: Ignoring unknown variable 'sin', in file , line 1.
If builtin functions were treated as literals, then that would be fine with
no warning, and x would then hold a FunctionType value.
The user would of course be free to assign a value to their own variable
named "sin", and this would shadow our hypothetical builtin function
literal of the same name.
It would be a weird thing for the user to do, but not ambiguous.
On Fri, Jul 31, 2020 at 6:35 PM adrianv avm4@cornell.edu wrote:
I hadn't really thought about how function literals might be involved
here.
But I'm a little puzzled. Suppose builtin functions can be treated as
literals as shown below. Then
foo = sin
is ambiguous about whether I mean the function "sin" or the variable
"sin".
I would think that if you want to make regular functions (and built-ins)
available as literals---which is a really good idea because otherwise it
creates a confusing situation where some functions need to be redefined as
function literals for convenience---you should do something like what
MATLAB
does and create a syntax for it. In MATLAB you create a function literal
(they call them handles) from (any) function using "@". So in MATLAB
foo = @sin
assigns a function literal into foo. And the call
foo(@sin,@cos)
passes two function literals into the function foo. Given how namespaces
work in OpenSCAD there's no way to assign a pre-existing function to a
variable or pass functions as literals without a new syntax that "converts"
the function into a literal.
But my point is that
foo=undef
is not any more ambiguous than
foo=sin
so it's not a problem that has to do with undef. So I still don't see any
issue with using the functional form I proposed.
foo = undef; // Plain undef without text
foo = undef("reason"); // undef with text
foo = @undef; // or whatever new syntax.... the undef function...though
why would you ever want that?
Have I missed something?
thehans wrote
As Doug points out, the situation is more complicated if you account for
function literals, which already exist as an experimental feature.
It's not necessarily an issue at the moment, but If we ever decided to
make
builtin functions accessible as literals (personally i would like to see
this), then writing: foo = undef; would be ambiguous whether you want the
function literal value or the actual undef literal.
On Fri, Jul 31, 2020 at 5:29 PM adrianv <
What is confusing about calling undef() like a function? You say we
don't
want to "mix up" the undef literal with an undef constructor, but what's
the
difference really between
undef
and
undef("some reason text")
Isn't the first one basically the same as the second one only with the
argument missing, in some sense? What's the problem or consequence of
"mixing up" these two things? When there's no string associated with
undef then what is there? Empty string? Another undef? It's a bit
unclear what the advantage is of not having a string. Shouldn't there
always be one? (undef: "list index out of bounds", undef: "variable not
set", undef: "mismatched argument types for +".) It seems like you'd
want
to encourage users to supply the string any time they make an
I guess there's the question of what it means if you just write
"foo=undef",
as in that case there's no string. The string could be "direct
assignment"
or something like that.
To me it seems like doing
foo = undef
but
foo = undefined("some reason")
is more confusing because now I have to remember when to use undef and
when
to use undefined. It looks like undef is already illegal as a function
name, so it wouldn't break any code, whereas undefined() is currently a
valid function, so using that could break code.
thehans wrote
No I haven't added any mechanism to query or set undefined strings
programmatically from scripts, since I felt like that needed some more
thought and planning around the interface, and wanted to limit this PR
only the least contentious changes.
Adding either of those should be fairly quick and simple to implement
though.
For displaying the string, i considered that echo() and str() could
possibly automatically show the undef strings if any. But this might
up with more verbose messages than people would like for general
Also as far as I know, echo() and str() generally work in such a way
you could copy/paste output back into a script and it should be parsed
into
the same literal values. So doing something like that should
probably require that we first lock in how the string would be set
programmatically (see below).
Having assert() handle undef as a special case might be one good way
inspect for reason strings though.
Another possibility is that the reason string could be a "member
on
undef types, so e.g. ( some_undef_value.reason ) could return a
But then should an undef without any string return undef for that as
well?(its undefs all the way down!)
And would ".reason" on a non-undef type also be undef, or throw an
error?
For setting the string, I don't think calling undef() like a function
good idea, since we don't want to mix up the default literal "undef"
identifier with a new builtin function(constructor basically)
of
the same name. But perhaps a function named undefined() would be
for
example.
On Fri, Jul 31, 2020 at 5:36 AM adrianv <
Another question: is there a way to create an undef and associate a
reason,
so for example
undef("line_intersection: lines did not intersect")
JordanBrown wrote
On 7/30/2020 9:24 PM, Hans L wrote:
I've now implemented the capability for undef types to carry a
Cool.
So if your program has an undef in its hands, is there a
way to say "what is the reason for this undef"?
I don't know whether it's necessary to have a way to retrieve the
reason, or just a way to say "hey, I don't want an undef, this is
error, report the error with the reason for the undef".
One really simple mechanism might be to have assert(undef) report
reason for the undef.
That would be an exception to the general rule that undef is OK in
boolean context, but then again undef is false in a boolean context
assert(false) is an error, so it's not stretching the rule very
The difference is that undef is an already established builtin *constant
value*, but sin is not.
x = sin;
WARNING: Ignoring unknown variable 'sin', in file , line 1.
If builtin functions were treated as literals, then that would be fine with
no warning, and x would then hold a FunctionType value.
The user would of course be free to assign a value to their own variable
named "sin", and this would shadow our hypothetical builtin function
literal of the same name.
It would be a weird thing for the user to do, but not ambiguous.
On Fri, Jul 31, 2020 at 6:35 PM adrianv <avm4@cornell.edu> wrote:
> I hadn't really thought about how function literals might be involved
> here.
> But I'm a little puzzled. Suppose builtin functions can be treated as
> literals as shown below. Then
>
> foo = sin
>
> is ambiguous about whether I mean the function "sin" or the variable
> "sin".
> I would think that if you want to make regular functions (and built-ins)
> available as literals---which is a really good idea because otherwise it
> creates a confusing situation where some functions need to be redefined as
> function literals for convenience---you should do something like what
> MATLAB
> does and create a syntax for it. In MATLAB you create a function literal
> (they call them handles) from (any) function using "@". So in MATLAB
>
> foo = @sin
>
> assigns a function literal into foo. And the call
>
> foo(@sin,@cos)
>
> passes two function literals into the function foo. Given how namespaces
> work in OpenSCAD there's no way to assign a pre-existing function to a
> variable or pass functions as literals without a new syntax that "converts"
> the function into a literal.
>
> But my point is that
>
> foo=undef
>
> is not any more ambiguous than
>
> foo=sin
>
> so it's not a problem that has to do with undef. So I still don't see any
> issue with using the functional form I proposed.
>
> foo = undef; // Plain undef without text
> foo = undef("reason"); // undef with text
> foo = @undef; // or whatever new syntax.... the undef function...though
> why would you ever want that?
>
> Have I missed something?
>
>
> thehans wrote
> > As Doug points out, the situation is more complicated if you account for
> > function literals, which already exist as an experimental feature.
> > It's not necessarily an issue at the moment, but If we ever decided to
> > make
> > builtin functions accessible as literals (personally i would like to see
> > this), then writing: foo = undef; would be ambiguous whether you want the
> > function literal value or the actual undef literal.
> >
> > On Fri, Jul 31, 2020 at 5:29 PM adrianv <
>
> > avm4@
>
> > > wrote:
> >
> >> What is confusing about calling undef() like a function? You say we
> >> don't
> >> want to "mix up" the undef literal with an undef constructor, but what's
> >> the
> >> difference really between
> >>
> >> undef
> >>
> >> and
> >>
> >> undef("some reason text")
> >>
> >> Isn't the first one basically the same as the second one only with the
> >> argument missing, in some sense? What's the problem or consequence of
> >> "mixing up" these two things? When there's no string associated with
> an
> >> undef then what is there? Empty string? Another undef? It's a bit
> >> unclear what the advantage is of not having a string. Shouldn't there
> >> always be one? (undef: "list index out of bounds", undef: "variable not
> >> set", undef: "mismatched argument types for +".) It seems like you'd
> >> want
> >> to encourage users to supply the string any time they make an
> assignment.
> >>
> >> I guess there's the question of what it means if you just write
> >> "foo=undef",
> >> as in that case there's no string. The string could be "direct
> >> assignment"
> >> or something like that.
> >>
> >> To me it seems like doing
> >>
> >> foo = undef
> >>
> >> but
> >>
> >> foo = undefined("some reason")
> >>
> >> is more confusing because now I have to remember when to use undef and
> >> when
> >> to use undefined. It looks like undef is already illegal as a function
> >> name, so it wouldn't break any code, whereas undefined() is currently a
> >> valid function, so using that could break code.
> >>
> >>
> >>
> >> thehans wrote
> >> > No I haven't added any mechanism to query or set undefined strings
> >> > programmatically from scripts, since I felt like that needed some more
> >> > thought and planning around the interface, and wanted to limit this PR
> >> to
> >> > only the least contentious changes.
> >> >
> >> > Adding either of those should be fairly quick and simple to implement
> >> > though.
> >> >
> >> > For displaying the string, i considered that echo() and str() could
> >> > possibly automatically show the undef strings if any. But this might
> >> end
> >> > up with more verbose messages than people would like for general
> cases.
> >> > Also as far as I know, echo() and str() generally work in such a way
> >> where
> >> > you could copy/paste output back into a script and it should be parsed
> >> > into
> >> > the same literal values. So doing something like that should
> >> > probably require that we first lock in how the string would be set
> >> > programmatically (see below).
> >> > Having assert() handle undef as a special case might be one good way
> to
> >> > inspect for reason strings though.
> >> >
> >> > Another possibility is that the reason string could be a "member
> >> lookup"
> >> > on
> >> > undef types, so e.g. ( some_undef_value.reason ) could return a
> string.
> >> > But then should an undef without any string return undef for that as
> >> > well?(its undefs all the way down!)
> >> > And would ".reason" on a non-undef type also be undef, or throw an
> >> actual
> >> > error?
> >> >
> >> > For setting the string, I don't think calling undef() like a function
> >> is
> >> a
> >> > good idea, since we don't want to mix up the default literal "undef"
> >> > identifier with a new builtin function(constructor basically)
> >> identifier
> >> > of
> >> > the same name. But perhaps a function named undefined() would be
> fine,
> >> > for
> >> > example.
> >> >
> >> >
> >> >
> >> >
> >> > On Fri, Jul 31, 2020 at 5:36 AM adrianv <
> >>
> >> > avm4@
> >>
> >> > > wrote:
> >> >
> >> >> Another question: is there a way to create an undef and associate a
> >> >> reason,
> >> >> so for example
> >> >>
> >> >> undef("line_intersection: lines did not intersect")
> >> >>
> >> >>
> >> >>
> >> >> JordanBrown wrote
> >> >> > On 7/30/2020 9:24 PM, Hans L wrote:
> >> >> >> I've now implemented the capability for undef types to carry a
> >> reason
> >> >> >> string.
> >> >> >
> >> >> > Cool.
> >> >> >
> >> >> > So if your program has an undef in its hands, is there a
> >> >> straightforward
> >> >> > way to say "what is the reason for this undef"?
> >> >> >
> >> >> > I don't know whether it's necessary to have a way to _retrieve_ the
> >> >> > reason, or just a way to say "hey, I don't want an undef, this is
> an
> >> >> > error, report the error with the reason for the undef".
> >> >> >
> >> >> > One really simple mechanism might be to have assert(undef) report
> >> the
> >> >> > reason for the undef.
> >> >> >
> >> >> > That would be an exception to the general rule that undef is OK in
> a
> >> >> > boolean context, but then again undef is false in a boolean context
> >> and
> >> >> > assert(false) is an error, so it's not stretching the rule very
> >> much.
> >> >> >
> >> >> >
> >> >> > _______________________________________________
> >> >> > 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@.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/
> >>
> >> _______________________________________________
> >> 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/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>