discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Adding an undef-coalescing operator '??'

KR
Katie Rust
Sat, Oct 19, 2024 10:13 PM

I created a PR for this on Github and was asked to solicit feedback on
the mailing list. Link to the PR:
https://github.com/openscad/openscad/pull/5383

Many languages have a similar operator, including C#, PHP, Perl,
Objective-C, and Javascript. Most use "??" although ObjC uses "?:" and
Perl uses "//". See
https://en.wikipedia.org/wiki/Null_coalescing_operator

It is defined as follows:

x ?? y evaluates x once, and if it is not undef, the result of
the expression is the value of x and y is not evaluated. If x is
undef, then y is evaluated, and the result is the value of y.

If x is a single variable lookup, not a compound expression, then
that lookup is performed without causing a warning if the lookup fails

  • the same behavior as the builtin is_undef function.

The operator's precedence is higher than the ternary operator x ? y : z but lower than the logical operators || and &&. It is also
right-associative, so x ?? y ?? z parses as x ?? (y ?? z). This is
the same as C#, PHP, and Javascript, although Javascript treats ??
and '||with the same precedence. I believe right-associativity is proper (even though the result would be the same either way) since the warning suppression wouldn't apply toyif it were parsed as(x ??
y) ?? z`.

Sources for operator precedence and associativity:
C#: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/
PHP: https://www.php.net/manual/en/language.operators.precedence.php
Javascript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence

Rationale:

The reason I wanted to add this operator, as I stated in the PR, is
because I wanted to add a compact way to provide a default for
variables which are possibly undefined at the point they are
referenced. Currently, the only way to do this is to use a ternary
construct with is_undef: !is_undef(var) ? var : default. This has
some drawbacks: The variable must be written out twice, and is looked
up twice, once by is_undef and again when the conditional evaluates
its ifexpr. I could define function default(x, y) = !is_undef(x) ? x : y, but in this case x and y are always evaluated before
calling the function, and if the variable lookup x fails, then it
causes a warning, and I set the option "Stop on the first warning" so
I don't get a cascade of warnings which I have to scroll through.

Other potential solutions:

  1. Add a built-in function default(x, y, ...) which takes any number
    of parameters, applying the same warning suppression, and returns the
    first one that is not undef. This isn't as compact as adding an
    operator but may be simpler.
  2. Suppress the undefined-variable warning until the undefined value
    is actually used for something other than a function parameter. This
    could be done by having Context::lookup_variable return
    Value::undef(...), although it can't return it as a reference, it
    would have to be changed to std::unique_ptr<Value> or something
    similar, and that could be a much bigger change.

I don't foresee any potential conflicts with adding this operator. The
only other syntax that uses "?" is the ternary operator, and "??" is
currently invalid syntax since there is no unary "?" operator.

Thoughts? Adding it as a built-in might be simpler, but I prefer the
compactness of the operator.

I created a PR for this on Github and was asked to solicit feedback on the mailing list. Link to the PR: https://github.com/openscad/openscad/pull/5383 Many languages have a similar operator, including C#, PHP, Perl, Objective-C, and Javascript. Most use "??" although ObjC uses "?:" and Perl uses "//". See https://en.wikipedia.org/wiki/Null_coalescing_operator It is defined as follows: `x ?? y` evaluates `x` once, and if it is not `undef`, the result of the expression is the value of `x` and `y` is not evaluated. If `x` is `undef`, then `y` is evaluated, and the result is the value of `y`. If `x` is a single variable lookup, not a compound expression, then that lookup is performed without causing a warning if the lookup fails - the same behavior as the builtin `is_undef` function. The operator's precedence is higher than the ternary operator `x ? y : z` but lower than the logical operators `||` and `&&`. It is also right-associative, so `x ?? y ?? z` parses as `x ?? (y ?? z)`. This is the same as C#, PHP, and Javascript, although Javascript treats `??` and '||` with the same precedence. I believe right-associativity is proper (even though the result would be the same either way) since the warning suppression wouldn't apply to `y` if it were parsed as `(x ?? y) ?? z`. Sources for operator precedence and associativity: C#: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/ PHP: https://www.php.net/manual/en/language.operators.precedence.php Javascript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence Rationale: The reason I wanted to add this operator, as I stated in the PR, is because I wanted to add a compact way to provide a default for variables which are possibly undefined at the point they are referenced. Currently, the only way to do this is to use a ternary construct with is_undef: `!is_undef(var) ? var : default`. This has some drawbacks: The variable must be written out twice, and is looked up twice, once by `is_undef` and again when the conditional evaluates its `ifexpr`. I could define `function default(x, y) = !is_undef(x) ? x : y`, but in this case `x` and `y` are always evaluated before calling the function, and if the variable lookup `x` fails, then it causes a warning, and I set the option "Stop on the first warning" so I don't get a cascade of warnings which I have to scroll through. Other potential solutions: 1. Add a built-in function `default(x, y, ...)` which takes any number of parameters, applying the same warning suppression, and returns the first one that is not `undef`. This isn't as compact as adding an operator but may be simpler. 2. Suppress the undefined-variable warning until the undefined value is actually used for something other than a function parameter. This could be done by having `Context::lookup_variable` return `Value::undef(...)`, although it can't return it as a reference, it would have to be changed to `std::unique_ptr<Value>` or something similar, and that could be a much bigger change. I don't foresee any potential conflicts with adding this operator. The only other syntax that uses "?" is the ternary operator, and "??" is currently invalid syntax since there is no unary "?" operator. Thoughts? Adding it as a built-in might be simpler, but I prefer the compactness of the operator.
JB
Jordan Brown
Sun, Oct 20, 2024 12:37 AM

Remember that "undefined variable" means a variable that has never been
assigned to.  A variable that has been set equal to undef is a defined
variable with the special undefined value.

I don't have a strong opinion on the feature, except for the magic
treatment of undefined variables.  It's terser than default(a, b), but
it's also yet another piece of syntax that a programmer must memorize.

The magic treatment of undefined variables gives me pause.  One of the
critically important features of language design is that it be
consistent.  Here that means that uttering a particular name should do
the same thing in every expression context.  We already have a violation
of that rule:  is_undef() is magic and suppresses undefined-variable
errors.  If you utter an undefined variable name in any other expression
context, you get an error; when you give one to this specific function
you don't.

Nobody will die, but every such magic case that you add makes the
language harder to understand.


Let's look at other languages for a moment.  (Again, "not declared or
initialized" is different from "initialized to the 'undefined" value".)

Perl... doesn't report undefined variables at all.

JavaScript... undefined variables are an error, even in ??.

PHP... ?? behaves as you describe.

C#... gives an error on undefined names, even with ??.

Objective-C... gives an error on undefined names, even with ?:.

Python... you don't mention it, but the "equivalent" is-None test gives
an error on undefined variables.

So while many languages have "if this variable exists and has a
non-null-ish value, use it, else use this other value" construct, few
will let it suppress an undefined variable error.  The only one that
checks for undefined variables at all and has this construct is PHP.


Turning undefined names into undef is in my opinion a total
non-starter.  It means that a typo is not detected immediately, but may
yield cascading errors much later and farther away from the error. 
There's a reason that almost every large-scale programming language
gives errors on undefined variables.


Do we need an easy way to detect and supply defaults for undefined
variables in OpenSCAD?

Well... maybe.

Inside one program, mostly I would say "no".  You control the horizontal
and the vertical. https://www.youtube.com/watch?v=8CtjhWhw2I8  Ensure
that stuff is initialized before you use it.

In a library the picture is different.  The library is separate from its
caller and doesn't want to tell the caller to initialize defaults.

In an "include"-style library, there is already something of an answer. 
Inclusion has a magic behavior (and again, magic is bad) that it's OK
for the main program to redefine a variable defined in the included
file.  Have the library set any global defaults that it wants, and then
have the caller override them if it wants to.  This works for both
lexical-scope and call-scope ($) variables.

In a "use"-style library, the caller's lexical-scope variables are not
accessible to the library, and vice versa, so for lexical-scope
variables there's no opportunity to need such a feature.  However, the
feature might be applicable to call-scope ($) variables.


Net..

Absent the undefined-variable magic, shrug.  I wouldn't add it, but I
also wouldn't object.

With the undefined-variable magic, I don't like it.  I won't stand in
front of a train to stop it, though.  We might need something better
than we have, in particular for $ variables being passed to libraries,
but I'm not convinced that this is the right answer there.

Remember that "undefined variable" means a variable that has never been assigned to.  A variable that has been set equal to undef is a defined variable with the special undefined value. I don't have a strong opinion on the feature, except for the magic treatment of undefined variables.  It's terser than default(a, b), but it's also yet another piece of syntax that a programmer must memorize. The magic treatment of undefined variables gives me pause.  One of the critically important features of language design is that it be consistent.  Here that means that uttering a particular name should do the same thing in every expression context.  We already have a violation of that rule:  is_undef() is magic and suppresses undefined-variable errors.  If you utter an undefined variable name in any other expression context, you get an error; when you give one to *this specific function* you don't. Nobody will die, but every such magic case that you add makes the language harder to understand. --- Let's look at other languages for a moment.  (Again, "not declared or initialized" is different from "initialized to the 'undefined" value".) Perl... doesn't report undefined variables at all. JavaScript... undefined variables are an error, even in ??. PHP... ?? behaves as you describe. C#... gives an error on undefined names, even with ??. Objective-C... gives an error on undefined names, even with ?:. Python... you don't mention it, but the "equivalent" is-None test gives an error on undefined variables. So while many languages have "if this variable exists and has a non-null-ish value, use it, else use this other value" construct, few will let it suppress an undefined variable error.  The only one that checks for undefined variables at all and has this construct is PHP. --- Turning undefined names into undef is in my opinion a total non-starter.  It means that a typo is not detected immediately, but may yield cascading errors much later and farther away from the error.  There's a reason that almost every large-scale programming language gives errors on undefined variables. --- Do we need an easy way to detect and supply defaults for undefined variables in OpenSCAD? Well... maybe. Inside one program, mostly I would say "no".  You control the horizontal and the vertical. <https://www.youtube.com/watch?v=8CtjhWhw2I8>  Ensure that stuff is initialized before you use it. In a library the picture is different.  The library is separate from its caller and doesn't want to tell the caller to initialize defaults. In an "include"-style library, there is already something of an answer.  Inclusion has a magic behavior (and again, magic is bad) that it's OK for the main program to redefine a variable defined in the included file.  Have the library set any global defaults that it wants, and then have the caller override them if it wants to.  This works for both lexical-scope and call-scope ($) variables. In a "use"-style library, the caller's lexical-scope variables are not accessible to the library, and vice versa, so for lexical-scope variables there's no opportunity to need such a feature.  However, the feature might be applicable to call-scope ($) variables. --- Net.. Absent the undefined-variable magic, shrug.  I wouldn't add it, but I also wouldn't object. With the undefined-variable magic, I don't like it.  I won't stand in front of a train to stop it, though.  We might need something better than we have, in particular for $ variables being passed to libraries, but I'm not convinced that this is the right answer there.
RD
Revar Desmera
Sun, Oct 20, 2024 2:20 AM

While one can easily make a default(x,dflt) function, which allows a compound initial value which evaluates exactly once, the alternate default value is forced to be evaluated even if the initial value was not undef. The proposed ?? operator fixes that issue.

The part of me that has written way too many thousand lines of bash code (which should probably be ignored) is screaming for a syntax using &amp;&amp; or ||. It should be doable, if you were to change &amp;&amp; from returning a boolean, to returning the first value if it evaluates as false, and the second value otherwise. For || return the first value if it evaluates as true, or the second value otherwise. This is partly based on the fact that undef evaluates as false. I’m fairly sure, though, that there’s a usage flaw there, if you actually want to return a boolean when an undef indicates failure.

-Revar

On Oct 19, 2024, at 5:38 PM, Jordan Brown via Discuss <discuss@lists.openscad.org> wrote:

  Remember that "undefined variable" means a variable that has never been assigned to. A variable that has been set equal to undef is a defined variable with the special undefined value.

I don't have a strong opinion on the feature, except for the magic treatment of undefined variables. It's terser than default(a, b), but it's also yet another piece of syntax that a programmer must memorize.

The magic treatment of undefined variables gives me pause. One of the critically important features of language design is that it be consistent. Here that means that uttering a particular name should do the same thing in every expression context. We already have a violation of that rule: is_undef() is magic and suppresses undefined-variable errors. If you utter an undefined variable name in any other expression context, you get an error; when you give one to this specific function you don't.

Nobody will die, but every such magic case that you add makes the language harder to understand.


Let's look at other languages for a moment. (Again, "not declared or initialized" is different from "initialized to the 'undefined" value".)

Perl... doesn't report undefined variables at all.

JavaScript... undefined variables are an error, even in ??.

PHP... ?? behaves as you describe.

C#... gives an error on undefined names, even with ??.

Objective-C... gives an error on undefined names, even with ?:.

Python... you don't mention it, but the "equivalent" is-None test gives an error on undefined variables.

So while many languages have "if this variable exists and has a non-null-ish value, use it, else use this other value" construct, few will let it suppress an undefined variable error. The only one that checks for undefined variables at all and has this construct is PHP.


Turning undefined names into undef is in my opinion a total non-starter. It means that a typo is not detected immediately, but may yield cascading errors much later and farther away from the error. There's a reason that almost every large-scale programming language gives errors on undefined variables.


Do we need an easy way to detect and supply defaults for undefined variables in OpenSCAD?

Well... maybe.

Inside one program, mostly I would say "no". You control the horizontal and the vertical. Ensure that stuff is initialized before you use it.

In a library the picture is different. The library is separate from its caller and doesn't want to tell the caller to initialize defaults.

In an "include"-style library, there is already something of an answer. Inclusion has a magic behavior (and again, magic is bad) that it's OK for the main program to redefine a variable defined in the included file. Have the library set any global defaults that it wants, and then have the caller override them if it wants to. This works for both lexical-scope and call-scope ($) variables.

In a "use"-style library, the caller's lexical-scope variables are not accessible to the library, and vice versa, so for lexical-scope variables there's no opportunity to need such a feature. However, the feature might be applicable to call-scope ($) variables.


Net..

Absent the undefined-variable magic, shrug. I wouldn't add it, but I also wouldn't object.

With the undefined-variable magic, I don't like it. I won't stand in front of a train to stop it, though. We might need something better than we have, in particular for $ variables being passed to libraries, but I'm not convinced that this is the right answer there.

_______________________________________________
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

KR
Katie Rust
Sun, Oct 20, 2024 8:27 AM

I definitely agree that the magic of suppressing undefined errors with
is_undef is kind of a hack, but there should be some way to suppress
the warning. In Python you can do a try: and except:, in
JavaScript, it's try / catch, same in C# if you have a dynamic
variable that you access ann undefined property on.

Implementing a full try / catch or warning suppression is probably not
easy and I'm not sure what value it would provide other than this one
specific case.

Your point about lexical variables is valid. Would changing it to only
suppress warnings for $ scoped variables be less of a hack, or would
it just be adding a special case on top of a special case?

On Sat, Oct 19, 2024 at 7:37 PM Jordan Brown
openscad@jordan.maileater.net wrote:

Remember that "undefined variable" means a variable that has never been assigned to.  A variable that has been set equal to undef is a defined variable with the special undefined value.

I don't have a strong opinion on the feature, except for the magic treatment of undefined variables.  It's terser than default(a, b), but it's also yet another piece of syntax that a programmer must memorize.

The magic treatment of undefined variables gives me pause.  One of the critically important features of language design is that it be consistent.  Here that means that uttering a particular name should do the same thing in every expression context.  We already have a violation of that rule:  is_undef() is magic and suppresses undefined-variable errors.  If you utter an undefined variable name in any other expression context, you get an error; when you give one to this specific function you don't.

Nobody will die, but every such magic case that you add makes the language harder to understand.


Let's look at other languages for a moment.  (Again, "not declared or initialized" is different from "initialized to the 'undefined" value".)

Perl... doesn't report undefined variables at all.

JavaScript... undefined variables are an error, even in ??.

PHP... ?? behaves as you describe.

C#... gives an error on undefined names, even with ??.

Objective-C... gives an error on undefined names, even with ?:.

Python... you don't mention it, but the "equivalent" is-None test gives an error on undefined variables.

So while many languages have "if this variable exists and has a non-null-ish value, use it, else use this other value" construct, few will let it suppress an undefined variable error.  The only one that checks for undefined variables at all and has this construct is PHP.


Turning undefined names into undef is in my opinion a total non-starter.  It means that a typo is not detected immediately, but may yield cascading errors much later and farther away from the error.  There's a reason that almost every large-scale programming language gives errors on undefined variables.


Do we need an easy way to detect and supply defaults for undefined variables in OpenSCAD?

Well... maybe.

Inside one program, mostly I would say "no".  You control the horizontal and the vertical.  Ensure that stuff is initialized before you use it.

In a library the picture is different.  The library is separate from its caller and doesn't want to tell the caller to initialize defaults.

In an "include"-style library, there is already something of an answer.  Inclusion has a magic behavior (and again, magic is bad) that it's OK for the main program to redefine a variable defined in the included file.  Have the library set any global defaults that it wants, and then have the caller override them if it wants to.  This works for both lexical-scope and call-scope ($) variables.

In a "use"-style library, the caller's lexical-scope variables are not accessible to the library, and vice versa, so for lexical-scope variables there's no opportunity to need such a feature.  However, the feature might be applicable to call-scope ($) variables.


Net..

Absent the undefined-variable magic, shrug.  I wouldn't add it, but I also wouldn't object.

With the undefined-variable magic, I don't like it.  I won't stand in front of a train to stop it, though.  We might need something better than we have, in particular for $ variables being passed to libraries, but I'm not convinced that this is the right answer there.

I definitely agree that the magic of suppressing undefined errors with is_undef is kind of a hack, but there should be some way to suppress the warning. In Python you can do a `try:` and `except:`, in JavaScript, it's `try` / `catch`, same in C# if you have a `dynamic` variable that you access ann undefined property on. Implementing a full try / catch or warning suppression is probably not easy and I'm not sure what value it would provide other than this one specific case. Your point about lexical variables is valid. Would changing it to only suppress warnings for `$` scoped variables be less of a hack, or would it just be adding a special case on top of a special case? On Sat, Oct 19, 2024 at 7:37 PM Jordan Brown <openscad@jordan.maileater.net> wrote: > > Remember that "undefined variable" means a variable that has never been assigned to. A variable that has been set equal to undef is a defined variable with the special undefined value. > > I don't have a strong opinion on the feature, except for the magic treatment of undefined variables. It's terser than default(a, b), but it's also yet another piece of syntax that a programmer must memorize. > > The magic treatment of undefined variables gives me pause. One of the critically important features of language design is that it be consistent. Here that means that uttering a particular name should do the same thing in every expression context. We already have a violation of that rule: is_undef() is magic and suppresses undefined-variable errors. If you utter an undefined variable name in any other expression context, you get an error; when you give one to *this specific function* you don't. > > Nobody will die, but every such magic case that you add makes the language harder to understand. > > --- > > Let's look at other languages for a moment. (Again, "not declared or initialized" is different from "initialized to the 'undefined" value".) > > Perl... doesn't report undefined variables at all. > > JavaScript... undefined variables are an error, even in ??. > > PHP... ?? behaves as you describe. > > C#... gives an error on undefined names, even with ??. > > Objective-C... gives an error on undefined names, even with ?:. > > Python... you don't mention it, but the "equivalent" is-None test gives an error on undefined variables. > > So while many languages have "if this variable exists and has a non-null-ish value, use it, else use this other value" construct, few will let it suppress an undefined variable error. The only one that checks for undefined variables at all and has this construct is PHP. > > --- > > Turning undefined names into undef is in my opinion a total non-starter. It means that a typo is not detected immediately, but may yield cascading errors much later and farther away from the error. There's a reason that almost every large-scale programming language gives errors on undefined variables. > > --- > > Do we need an easy way to detect and supply defaults for undefined variables in OpenSCAD? > > Well... maybe. > > Inside one program, mostly I would say "no". You control the horizontal and the vertical. Ensure that stuff is initialized before you use it. > > In a library the picture is different. The library is separate from its caller and doesn't want to tell the caller to initialize defaults. > > In an "include"-style library, there is already something of an answer. Inclusion has a magic behavior (and again, magic is bad) that it's OK for the main program to redefine a variable defined in the included file. Have the library set any global defaults that it wants, and then have the caller override them if it wants to. This works for both lexical-scope and call-scope ($) variables. > > In a "use"-style library, the caller's lexical-scope variables are not accessible to the library, and vice versa, so for lexical-scope variables there's no opportunity to need such a feature. However, the feature might be applicable to call-scope ($) variables. > > --- > > Net.. > > Absent the undefined-variable magic, shrug. I wouldn't add it, but I also wouldn't object. > > With the undefined-variable magic, I don't like it. I won't stand in front of a train to stop it, though. We might need something better than we have, in particular for $ variables being passed to libraries, but I'm not convinced that this is the right answer there. >
NH
nop head
Sun, Oct 20, 2024 11:43 AM

I have a large project split into multiple files. The main.scad defines
various variables to affect the view and they are acted upon in the
individual files but any of those can be opened to show a sub-assembly and
of course the variables are not defined so I have lots of  X =
(is_undef($X) ? X : $X) * X_travel / 2; where X is local to the file and
can be set by the customiser when the sub assembly is viewed but $X is
passed by main.scad and overrides it and is set by the customiser when the
main assembly is viewed. Note I don't use the customiser to customise
printed parts. I use it to pose assembly views.

So a ?? operator would be nice in my opinion.

On Sun, 20 Oct 2024 at 09:27, Katie Rust via Discuss <
discuss@lists.openscad.org> wrote:

I definitely agree that the magic of suppressing undefined errors with
is_undef is kind of a hack, but there should be some way to suppress
the warning. In Python you can do a try: and except:, in
JavaScript, it's try / catch, same in C# if you have a dynamic
variable that you access ann undefined property on.

Implementing a full try / catch or warning suppression is probably not
easy and I'm not sure what value it would provide other than this one
specific case.

Your point about lexical variables is valid. Would changing it to only
suppress warnings for $ scoped variables be less of a hack, or would
it just be adding a special case on top of a special case?

On Sat, Oct 19, 2024 at 7:37 PM Jordan Brown
openscad@jordan.maileater.net wrote:

Remember that "undefined variable" means a variable that has never been

assigned to.  A variable that has been set equal to undef is a defined
variable with the special undefined value.

I don't have a strong opinion on the feature, except for the magic

treatment of undefined variables.  It's terser than default(a, b), but it's
also yet another piece of syntax that a programmer must memorize.

The magic treatment of undefined variables gives me pause.  One of the

critically important features of language design is that it be consistent.
Here that means that uttering a particular name should do the same thing in
every expression context.  We already have a violation of that rule:
is_undef() is magic and suppresses undefined-variable errors.  If you utter
an undefined variable name in any other expression context, you get an
error; when you give one to this specific function you don't.

Nobody will die, but every such magic case that you add makes the

language harder to understand.


Let's look at other languages for a moment.  (Again, "not declared or

initialized" is different from "initialized to the 'undefined" value".)

Perl... doesn't report undefined variables at all.

JavaScript... undefined variables are an error, even in ??.

PHP... ?? behaves as you describe.

C#... gives an error on undefined names, even with ??.

Objective-C... gives an error on undefined names, even with ?:.

Python... you don't mention it, but the "equivalent" is-None test gives

an error on undefined variables.

So while many languages have "if this variable exists and has a

non-null-ish value, use it, else use this other value" construct, few will
let it suppress an undefined variable error.  The only one that checks for
undefined variables at all and has this construct is PHP.


Turning undefined names into undef is in my opinion a total

non-starter.  It means that a typo is not detected immediately, but may
yield cascading errors much later and farther away from the error.  There's
a reason that almost every large-scale programming language gives errors on
undefined variables.


Do we need an easy way to detect and supply defaults for undefined

variables in OpenSCAD?

Well... maybe.

Inside one program, mostly I would say "no".  You control the horizontal

and the vertical.  Ensure that stuff is initialized before you use it.

In a library the picture is different.  The library is separate from its

caller and doesn't want to tell the caller to initialize defaults.

In an "include"-style library, there is already something of an answer.

Inclusion has a magic behavior (and again, magic is bad) that it's OK for
the main program to redefine a variable defined in the included file.  Have
the library set any global defaults that it wants, and then have the caller
override them if it wants to.  This works for both lexical-scope and
call-scope ($) variables.

In a "use"-style library, the caller's lexical-scope variables are not

accessible to the library, and vice versa, so for lexical-scope variables
there's no opportunity to need such a feature.  However, the feature might
be applicable to call-scope ($) variables.


Net..

Absent the undefined-variable magic, shrug.  I wouldn't add it, but I

also wouldn't object.

With the undefined-variable magic, I don't like it.  I won't stand in

front of a train to stop it, though.  We might need something better than
we have, in particular for $ variables being passed to libraries, but I'm
not convinced that this is the right answer there.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

I have a large project split into multiple files. The main.scad defines various variables to affect the view and they are acted upon in the individual files but any of those can be opened to show a sub-assembly and of course the variables are not defined so I have lots of X = (is_undef($X) ? X : $X) * X_travel / 2; where X is local to the file and can be set by the customiser when the sub assembly is viewed but $X is passed by main.scad and overrides it and is set by the customiser when the main assembly is viewed. Note I don't use the customiser to customise printed parts. I use it to pose assembly views. So a ?? operator would be nice in my opinion. On Sun, 20 Oct 2024 at 09:27, Katie Rust via Discuss < discuss@lists.openscad.org> wrote: > I definitely agree that the magic of suppressing undefined errors with > is_undef is kind of a hack, but there should be some way to suppress > the warning. In Python you can do a `try:` and `except:`, in > JavaScript, it's `try` / `catch`, same in C# if you have a `dynamic` > variable that you access ann undefined property on. > > Implementing a full try / catch or warning suppression is probably not > easy and I'm not sure what value it would provide other than this one > specific case. > > Your point about lexical variables is valid. Would changing it to only > suppress warnings for `$` scoped variables be less of a hack, or would > it just be adding a special case on top of a special case? > > > On Sat, Oct 19, 2024 at 7:37 PM Jordan Brown > <openscad@jordan.maileater.net> wrote: > > > > Remember that "undefined variable" means a variable that has never been > assigned to. A variable that has been set equal to undef is a defined > variable with the special undefined value. > > > > I don't have a strong opinion on the feature, except for the magic > treatment of undefined variables. It's terser than default(a, b), but it's > also yet another piece of syntax that a programmer must memorize. > > > > The magic treatment of undefined variables gives me pause. One of the > critically important features of language design is that it be consistent. > Here that means that uttering a particular name should do the same thing in > every expression context. We already have a violation of that rule: > is_undef() is magic and suppresses undefined-variable errors. If you utter > an undefined variable name in any other expression context, you get an > error; when you give one to *this specific function* you don't. > > > > Nobody will die, but every such magic case that you add makes the > language harder to understand. > > > > --- > > > > Let's look at other languages for a moment. (Again, "not declared or > initialized" is different from "initialized to the 'undefined" value".) > > > > Perl... doesn't report undefined variables at all. > > > > JavaScript... undefined variables are an error, even in ??. > > > > PHP... ?? behaves as you describe. > > > > C#... gives an error on undefined names, even with ??. > > > > Objective-C... gives an error on undefined names, even with ?:. > > > > Python... you don't mention it, but the "equivalent" is-None test gives > an error on undefined variables. > > > > So while many languages have "if this variable exists and has a > non-null-ish value, use it, else use this other value" construct, few will > let it suppress an undefined variable error. The only one that checks for > undefined variables at all and has this construct is PHP. > > > > --- > > > > Turning undefined names into undef is in my opinion a total > non-starter. It means that a typo is not detected immediately, but may > yield cascading errors much later and farther away from the error. There's > a reason that almost every large-scale programming language gives errors on > undefined variables. > > > > --- > > > > Do we need an easy way to detect and supply defaults for undefined > variables in OpenSCAD? > > > > Well... maybe. > > > > Inside one program, mostly I would say "no". You control the horizontal > and the vertical. Ensure that stuff is initialized before you use it. > > > > In a library the picture is different. The library is separate from its > caller and doesn't want to tell the caller to initialize defaults. > > > > In an "include"-style library, there is already something of an answer. > Inclusion has a magic behavior (and again, magic is bad) that it's OK for > the main program to redefine a variable defined in the included file. Have > the library set any global defaults that it wants, and then have the caller > override them if it wants to. This works for both lexical-scope and > call-scope ($) variables. > > > > In a "use"-style library, the caller's lexical-scope variables are not > accessible to the library, and vice versa, so for lexical-scope variables > there's no opportunity to need such a feature. However, the feature might > be applicable to call-scope ($) variables. > > > > --- > > > > Net.. > > > > Absent the undefined-variable magic, shrug. I wouldn't add it, but I > also wouldn't object. > > > > With the undefined-variable magic, I don't like it. I won't stand in > front of a train to stop it, though. We might need something better than > we have, in particular for $ variables being passed to libraries, but I'm > not convinced that this is the right answer there. > > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
RW
Raymond West
Sun, Oct 20, 2024 1:29 PM

Coming from a procedural programming background ( IBM Fortran IV, 60
years ago), I tend to view undefined values as potential sources of
error, so I instinctively prefer assigning values to variables upon
creation. I realize this might reflect my procedural mindset, but it
helps me maintain clarity and predictability in my code.

On 20/10/2024 12:43, nop head via Discuss wrote:

I have a large project split into multiple files. The main.scad
defines various variables to affect the view and they are acted upon
in the individual files but any of those can be opened to show a
sub-assembly and of course the variables are not defined so I have
lots of  X = (is_undef($X) ? X : $X) * X_travel / 2; where X is local
to the file and can be set by the customiser when the sub assembly is
viewed but $X is passed by main.scad and overrides it and is set by
the customiser when the main assembly is viewed. Note I don't use the
customiser to customise printed parts. I use it to pose assembly views.

Coming from a procedural programming background ( IBM Fortran IV, 60 years ago), I tend to view undefined values as potential sources of error, so I instinctively prefer assigning values to variables upon creation. I realize this might reflect my procedural mindset, but it helps me maintain clarity and predictability in my code. On 20/10/2024 12:43, nop head via Discuss wrote: > I have a large project split into multiple files. The main.scad > defines various variables to affect the view and they are acted upon > in the individual files but any of those can be opened to show a > sub-assembly and of course the variables are not defined so I have > lots of  X = (is_undef($X) ? X : $X) * X_travel / 2; where X is local > to the file and can be set by the customiser when the sub assembly is > viewed but $X is passed by main.scad and overrides it and is set by > the customiser when the main assembly is viewed. Note I don't use the > customiser to customise printed parts. I use it to pose assembly views.
NH
nop head
Sun, Oct 20, 2024 1:37 PM

But that isn't possible with my multi-file projects. Each assembly needs to
work alone but also be able to be used in the main application and its
customiser values overridden from there. So I definitely need to test if a
variable from the main application is present and use it, else use the
local value. The $variable from the main file may be present or not.

On Sun, 20 Oct 2024 at 14:30, Raymond West via Discuss <
discuss@lists.openscad.org> wrote:

Coming from a procedural programming background ( IBM Fortran IV, 60
years ago), I tend to view undefined values as potential sources of
error, so I instinctively prefer assigning values to variables upon
creation. I realize this might reflect my procedural mindset, but it
helps me maintain clarity and predictability in my code.

On 20/10/2024 12:43, nop head via Discuss wrote:

I have a large project split into multiple files. The main.scad
defines various variables to affect the view and they are acted upon
in the individual files but any of those can be opened to show a
sub-assembly and of course the variables are not defined so I have
lots of  X = (is_undef($X) ? X : $X) * X_travel / 2; where X is local
to the file and can be set by the customiser when the sub assembly is
viewed but $X is passed by main.scad and overrides it and is set by
the customiser when the main assembly is viewed. Note I don't use the
customiser to customise printed parts. I use it to pose assembly views.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

But that isn't possible with my multi-file projects. Each assembly needs to work alone but also be able to be used in the main application and its customiser values overridden from there. So I definitely need to test if a variable from the main application is present and use it, else use the local value. The $variable from the main file may be present or not. On Sun, 20 Oct 2024 at 14:30, Raymond West via Discuss < discuss@lists.openscad.org> wrote: > Coming from a procedural programming background ( IBM Fortran IV, 60 > years ago), I tend to view undefined values as potential sources of > error, so I instinctively prefer assigning values to variables upon > creation. I realize this might reflect my procedural mindset, but it > helps me maintain clarity and predictability in my code. > > On 20/10/2024 12:43, nop head via Discuss wrote: > > I have a large project split into multiple files. The main.scad > > defines various variables to affect the view and they are acted upon > > in the individual files but any of those can be opened to show a > > sub-assembly and of course the variables are not defined so I have > > lots of X = (is_undef($X) ? X : $X) * X_travel / 2; where X is local > > to the file and can be set by the customiser when the sub assembly is > > viewed but $X is passed by main.scad and overrides it and is set by > > the customiser when the main assembly is viewed. Note I don't use the > > customiser to customise printed parts. I use it to pose assembly views. > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
JB
Jordan Brown
Sun, Oct 20, 2024 4:55 PM

On 10/19/2024 7:20 PM, Revar Desmera via Discuss wrote:

While one can easily make a default(x,dflt) function, which allows a
compound initial value which evaluates exactly once, the alternate
default value is forced to be evaluated even if the initial value was
not undef. The proposed ?? operator fixes that issue.

Indeed.

The part of me that has written way too many thousand lines of bash
code (which should probably be ignored) is screaming for a syntax
using && or ||.  It should be doable, if you were to change &&
from returning a boolean, to returning the first value if it evaluates
as false, and the second value otherwise. For || return the first
value if it evaluates as true, or the second value otherwise.  This is
partly based on the fact that undef evaluates as false.  I’m fairly
sure, though, that there’s a usage flaw there, if you actually want to
return a boolean when an undef indicates failure.

That's how JavaScript && and || are defined.  It's a very useful
definition for ||; less so for &&.  (Less so for && because it returns
the first falsey value, and there aren't very many different falsey
values.)  It was my first thought.

There are three problems with just adopting that:

  • On its face, it doesn't address Katie's desire to provide a default
    for a variable that isn't defined, that has never been assigned to.
  • It's not backward compatible; existing OpenSCAD returns Boolean
    values from && and ||.  Theoretically somebody could be depending on
    that conversion.
  • && and || operate on truth and falsehood, and so undef, false, zero,
    empty strings, and empty arrays are all considered false.  As I
    understand it, ?? detects undefinedness (however defined).
On 10/19/2024 7:20 PM, Revar Desmera via Discuss wrote: > While one can easily make a `default(x,dflt)` function, which allows a > compound initial value which evaluates exactly once, the alternate > default value is forced to be evaluated even if the initial value was > not undef. The proposed `??` operator fixes that issue. Indeed. > The part of me that has written way too many thousand lines of bash > code (which should probably be ignored) is screaming for a syntax > using `&&` or `||`.  It should be doable, if you were to change `&&` > from returning a boolean, to returning the first value if it evaluates > as false, and the second value otherwise. For `||` return the first > value if it evaluates as true, or the second value otherwise.  This is > partly based on the fact that `undef` evaluates as false.  I’m fairly > sure, though, that there’s a usage flaw there, if you actually want to > return a boolean when an undef indicates failure. That's how JavaScript && and || are defined.  It's a very useful definition for ||; less so for &&.  (Less so for && because it returns the first falsey value, and there aren't very many different falsey values.)  It was my first thought. There are three problems with just adopting that: * On its face, it doesn't address Katie's desire to provide a default for a variable that isn't defined, that has never been assigned to. * It's not backward compatible; existing OpenSCAD returns Boolean values from && and ||.  Theoretically somebody could be depending on that conversion. * && and || operate on truth and falsehood, and so undef, false, zero, empty strings, and empty arrays are all considered false.  As I understand it, ?? detects undefinedness (however defined).
RW
Raymond West
Sun, Oct 20, 2024 5:59 PM

undef was not available in fortran.  We used to use -99 quite a lot 🙂,
but then of course variable names could be re-used, which solved many
problems (and created a few, since variable names were restricted to 4
characters in length. I still use i to n for loop counters - usually j
or k, maybe i or n, but never l or m for some reason).

On 20/10/2024 14:37, nop head via Discuss wrote:

But that isn't possible with my multi-file projects. Each assembly
needs to work alone but also be able to be used in the main
application and its customiser values overridden from there. So I
definitely need to test if a variable from the main application is
present and use it, else use the local value. The $variable from the
main file may be present or not.

On Sun, 20 Oct 2024 at 14:30, Raymond West via Discuss
discuss@lists.openscad.org wrote:

 Coming from a procedural programming background ( IBM Fortran IV, 60
 years ago), I tend to view undefined values as potential sources of
 error, so I instinctively prefer assigning values to variables upon
 creation. I realize this might reflect my procedural mindset, but it
 helps me maintain clarity and predictability in my code.

 On 20/10/2024 12:43, nop head via Discuss wrote:

I have a large project split into multiple files. The main.scad
defines various variables to affect the view and they are acted

 upon

in the individual files but any of those can be opened to show a
sub-assembly and of course the variables are not defined so I have
lots of  X = (is_undef($X) ? X : $X) * X_travel / 2; where X is

 local

to the file and can be set by the customiser when the sub

 assembly is

viewed but $X is passed by main.scad and overrides it and is set by
the customiser when the main assembly is viewed. Note I don't

 use the

customiser to customise printed parts. I use it to pose assembly

 views.
 _______________________________________________
 OpenSCAD mailing list
 To unsubscribe send an email to discuss-leave@lists.openscad.org

OpenSCAD mailing list
To unsubscribe send an email todiscuss-leave@lists.openscad.org

undef was not available in fortran.  We used to use -99 quite a lot 🙂, but then of course variable names could be re-used, which solved many problems (and created a few, since variable names were restricted to 4 characters in length. I still use i to n for loop counters - usually j or k, maybe i or n, but never l or m for some reason). On 20/10/2024 14:37, nop head via Discuss wrote: > But that isn't possible with my multi-file projects. Each assembly > needs to work alone but also be able to be used in the main > application and its customiser values overridden from there. So I > definitely need to test if a variable from the main application is > present and use it, else use the local value. The $variable from the > main file may be present or not. > > On Sun, 20 Oct 2024 at 14:30, Raymond West via Discuss > <discuss@lists.openscad.org> wrote: > > Coming from a procedural programming background ( IBM Fortran IV, 60 > years ago), I tend to view undefined values as potential sources of > error, so I instinctively prefer assigning values to variables upon > creation. I realize this might reflect my procedural mindset, but it > helps me maintain clarity and predictability in my code. > > On 20/10/2024 12:43, nop head via Discuss wrote: > > I have a large project split into multiple files. The main.scad > > defines various variables to affect the view and they are acted > upon > > in the individual files but any of those can be opened to show a > > sub-assembly and of course the variables are not defined so I have > > lots of  X = (is_undef($X) ? X : $X) * X_travel / 2; where X is > local > > to the file and can be set by the customiser when the sub > assembly is > > viewed but $X is passed by main.scad and overrides it and is set by > > the customiser when the main assembly is viewed. Note I don't > use the > > customiser to customise printed parts. I use it to pose assembly > views. > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org > > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email todiscuss-leave@lists.openscad.org
JD
John David
Sun, Oct 20, 2024 9:24 PM

"undef was not available in fortran... since variable names were restricted
to 4 characters in length..."

That brings back some memories...  One of my favorites is that the
following is, or at least was, a valid program in Fortran 66 and 77:

   Reality, Sanity, and Me
   STOP
   END

I do the same with i, j, k, and l.  But the underlying issue here is that
variables cannot be updated once set in a given context.  This has always
thrown me in OpenSCAD.

On Sun, Oct 20, 2024 at 1:59 PM Raymond West via Discuss <
discuss@lists.openscad.org> wrote:

undef was not available in fortran.  We used to use -99 quite a lot 🙂,
but then of course variable names could be re-used, which solved many
problems (and created a few, since variable names were restricted to 4
characters in length. I still use i to n for loop counters - usually j or
k, maybe i or n, but never l or m for some reason).
On 20/10/2024 14:37, nop head via Discuss wrote:

But that isn't possible with my multi-file projects. Each assembly needs
to work alone but also be able to be used in the main application and its
customiser values overridden from there. So I definitely need to test if a
variable from the main application is present and use it, else use the
local value. The $variable from the main file may be present or not.

On Sun, 20 Oct 2024 at 14:30, Raymond West via Discuss <
discuss@lists.openscad.org> wrote:

Coming from a procedural programming background ( IBM Fortran IV, 60
years ago), I tend to view undefined values as potential sources of
error, so I instinctively prefer assigning values to variables upon
creation. I realize this might reflect my procedural mindset, but it
helps me maintain clarity and predictability in my code.

On 20/10/2024 12:43, nop head via Discuss wrote:

I have a large project split into multiple files. The main.scad
defines various variables to affect the view and they are acted upon
in the individual files but any of those can be opened to show a
sub-assembly and of course the variables are not defined so I have
lots of  X = (is_undef($X) ? X : $X) * X_travel / 2; where X is local
to the file and can be set by the customiser when the sub assembly is
viewed but $X is passed by main.scad and overrides it and is set by
the customiser when the main assembly is viewed. Note I don't use the
customiser to customise printed parts. I use it to pose assembly views.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

"undef was not available in fortran... since variable names were restricted to 4 characters in length..." That brings back some memories... One of my favorites is that the following is, or at least was, a valid program in Fortran 66 and 77: Reality, Sanity, and Me STOP END I do the same with i, j, k, and l. But the underlying issue here is that variables cannot be updated once set in a given context. This has always thrown me in OpenSCAD. On Sun, Oct 20, 2024 at 1:59 PM Raymond West via Discuss < discuss@lists.openscad.org> wrote: > undef was not available in fortran. We used to use -99 quite a lot 🙂, > but then of course variable names could be re-used, which solved many > problems (and created a few, since variable names were restricted to 4 > characters in length. I still use i to n for loop counters - usually j or > k, maybe i or n, but never l or m for some reason). > On 20/10/2024 14:37, nop head via Discuss wrote: > > But that isn't possible with my multi-file projects. Each assembly needs > to work alone but also be able to be used in the main application and its > customiser values overridden from there. So I definitely need to test if a > variable from the main application is present and use it, else use the > local value. The $variable from the main file may be present or not. > > On Sun, 20 Oct 2024 at 14:30, Raymond West via Discuss < > discuss@lists.openscad.org> wrote: > >> Coming from a procedural programming background ( IBM Fortran IV, 60 >> years ago), I tend to view undefined values as potential sources of >> error, so I instinctively prefer assigning values to variables upon >> creation. I realize this might reflect my procedural mindset, but it >> helps me maintain clarity and predictability in my code. >> >> On 20/10/2024 12:43, nop head via Discuss wrote: >> > I have a large project split into multiple files. The main.scad >> > defines various variables to affect the view and they are acted upon >> > in the individual files but any of those can be opened to show a >> > sub-assembly and of course the variables are not defined so I have >> > lots of X = (is_undef($X) ? X : $X) * X_travel / 2; where X is local >> > to the file and can be set by the customiser when the sub assembly is >> > viewed but $X is passed by main.scad and overrides it and is set by >> > the customiser when the main assembly is viewed. Note I don't use the >> > customiser to customise printed parts. I use it to pose assembly views. >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >