discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

What happened to booleans?

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

Discuss@.openscad

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

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/

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

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

--
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:

  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

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 >