www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The new ?? and ??? operators

reply Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
It would be the sugar syntactic to add '??' operator to D. Consider the example
code:

string error_message = getErrorMessage() ?? "no errors";
A a = x.getPreparedAObject() ?? y.getPreparedAObject() ?? new A();


the first non-null value or to null if all rvalues contains null. This operator
might be extended to '???' to evaluate to a value that conforms to some
conditions. The lvalue gets the first rvalue that evaluates to true (evaluation
goes from left to right). If no conditions evaluates to true then the lvalue
stays unchanged. If no conditions are given explicitly then those conditions
evaluates to true (so, the best place for them is at the end of a sequence).

For instance:

int codeval = getValue1() > 5 ??? getValue2() >= 4 ??? getValue3() != 0 ??? 1;
// if no conditions are satisfied then codeval = 1 as a default value at last.
There are no sane reasons to place it in the middle of the sequence.

int code = 7;
code = 5 < 10 ??? 0 != 0; // this way, code = 7 (no conditions are satisfied)
Sep 23 2007
next sibling parent reply "Vladimir Panteleev" <thecybershadow gmail.com> writes:
On Sun, 23 Sep 2007 18:34:09 +0300, Arlen Albert Keshabyan <arlen.albert=
 gmail.com> wrote:

[regarding ???]
 The lvalue gets the first rvalue that evaluates to true (evaluation go=
es from left to right). = So the result can only be boolean? In this case, I don't understand how a =3D b ??? c ??? d; is different from a |=3D b || c || d; If b, c and d are false then a is unchanged. -- = Best regards, Vladimir mailto:thecybershadow gmail.com
Sep 23 2007
next sibling parent reply Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Vladimir Panteleev Wrote:

 On Sun, 23 Sep 2007 18:34:09 +0300, Arlen Albert Keshabyan
<arlen.albert gmail.com> wrote:
 
 [regarding ???]
 The lvalue gets the first rvalue that evaluates to true (evaluation goes from
left to right). 
So the result can only be boolean? In this case, I don't understand how a = b ??? c ??? d; is different from a |= b || c || d; If b, c and d are false then a is unchanged. -- Best regards, Vladimir mailto:thecybershadow gmail.com
Consider this: int v = 15; int v2 = 30; int a = v > 20 ??? v = 17 ??? v2 < 25 ??? 5; in this case 'a' evaluates to 5 because none of the conditions evaluate to true except the last one. if you remove the last condition (??? 5) then 'a' stays 0 in this case. If v2 = 23 then 'a' evaluates to 23; If v = 27 then 'a' evaluates to 27; etc. It means that it takes a sequential rvalue, tests it against a condition and if the condition evaluates to true then it assigns that rvalue to lvalue. It does not mean you are restricted to boolean values only.
Sep 23 2007
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Arlen Albert Keshabyan wrote:
 Vladimir Panteleev Wrote:
 
 On Sun, 23 Sep 2007 18:34:09 +0300, Arlen Albert Keshabyan
<arlen.albert gmail.com> wrote:

 [regarding ???]
 The lvalue gets the first rvalue that evaluates to true (evaluation goes from
left to right). 
So the result can only be boolean? In this case, I don't understand how a = b ??? c ??? d; is different from a |= b || c || d; If b, c and d are false then a is unchanged. -- Best regards, Vladimir mailto:thecybershadow gmail.com
Consider this: int v = 15; int v2 = 30; int a = v > 20 ??? v = 17 ??? v2 < 25 ??? 5; in this case 'a' evaluates to 5 because none of the conditions evaluate to true except the last one. if you remove the last condition (??? 5) then 'a' stays 0 in this case. If v2 = 23 then 'a' evaluates to 23; If v = 27 then 'a' evaluates to 27; etc. It means that it takes a sequential rvalue, tests it against a condition and if the condition evaluates to true then it assigns that rvalue to lvalue. It does not mean you are restricted to boolean values only.
Or in other words this: int a = v > 20 ??? v == 17 ??? v2 < 25 ??? 5 ; Is supposed to be shorthand for this? int a; if (v > 20 ||v == 17) a = v; else if (v2 < 25) a = v2; else a = 5; ...its an interesting thought, but it just feels more like a scripting language feature than that of a systems language. The other '??' version that just skips nulls could be useful at times, though. Hmm. -- Chris Nicholson-Sauls
Sep 23 2007
next sibling parent reply "Vladimir Panteleev" <thecybershadow gmail.com> writes:
On Sun, 23 Sep 2007 20:47:08 +0300, Chris Nicholson-Sauls
<ibisbasenji gmail.com> wrote:

 ...its an interesting thought, but it just feels more like a scripting
language feature than that of a systems language.  The other '??' version that
just skips nulls could be useful at times, though.  Hmm.
I think this is possible to implement via templates - or, if not, it will be with macros (in the form of "firstNonNull(b, c, d)"). -- Best regards, Vladimir mailto:thecybershadow gmail.com
Sep 23 2007
parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Vladimir Panteleev Wrote:

 On Sun, 23 Sep 2007 20:47:08 +0300, Chris Nicholson-Sauls
<ibisbasenji gmail.com> wrote:
 
 ...its an interesting thought, but it just feels more like a scripting
language feature than that of a systems language.  The other '??' version that
just skips nulls could be useful at times, though.  Hmm.
I think this is possible to implement via templates - or, if not, it will be with macros (in the form of "firstNonNull(b, c, d)"). -- Best regards, Vladimir mailto:thecybershadow gmail.com
Well, maybe you're right.
Sep 23 2007
prev sibling next sibling parent Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Chris Nicholson-Sauls wrote:

 Or in other words this:
 
 int a = v   > 20
      ??? v  == 17
      ??? v2  < 25
      ??? 5
 ;
 
 Is supposed to be shorthand for this?
 
 int a;
 if (v  > 20 ||v == 17)
      a = v;
 else if (v2 < 25)
      a = v2;
 else
      a = 5;
 
D could have if and case expressions instead of statements for handling these. Something like Haskell guards and cases: http://haskell.org/tutorial/patterns.html#sect4.3 http://haskell.org/tutorial/patterns.html#sect4.1 Or if the coming metaprogramming capabilities will be strong enough, those could be possible to implement on the library level, which would be nice too (yes, mixin+ctfe makes them already possible, but is ugly).
 The other '??' version that just skips nulls could be
 useful at times, though.
But it's limited to this one tiny purpose.
Sep 23 2007
prev sibling parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Chris Nicholson-Sauls Wrote:

 Arlen Albert Keshabyan wrote:
 Vladimir Panteleev Wrote:
 
 On Sun, 23 Sep 2007 18:34:09 +0300, Arlen Albert Keshabyan
<arlen.albert gmail.com> wrote:

 [regarding ???]
 The lvalue gets the first rvalue that evaluates to true (evaluation goes from
left to right). 
So the result can only be boolean? In this case, I don't understand how a = b ??? c ??? d; is different from a |= b || c || d; If b, c and d are false then a is unchanged. -- Best regards, Vladimir mailto:thecybershadow gmail.com
Consider this: int v = 15; int v2 = 30; int a = v > 20 ??? v = 17 ??? v2 < 25 ??? 5; in this case 'a' evaluates to 5 because none of the conditions evaluate to true except the last one. if you remove the last condition (??? 5) then 'a' stays 0 in this case. If v2 = 23 then 'a' evaluates to 23; If v = 27 then 'a' evaluates to 27; etc. It means that it takes a sequential rvalue, tests it against a condition and if the condition evaluates to true then it assigns that rvalue to lvalue. It does not mean you are restricted to boolean values only.
Or in other words this: int a = v > 20 ??? v == 17 ??? v2 < 25 ??? 5 ; Is supposed to be shorthand for this? int a; if (v > 20 ||v == 17) a = v; else if (v2 < 25) a = v2; else a = 5; ...its an interesting thought, but it just feels more like a scripting language feature than that of a systems language. The other '??' version that just skips nulls could be useful at times, though. Hmm. -- Chris Nicholson-Sauls
Yes. Exactly.
Sep 23 2007
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/23/07, Arlen Albert Keshabyan <arlen.albert gmail.com> wrote:
 Consider this:

 int v = 15;
 int v2 = 30;

 int a = v > 20 ??? v = 17 ??? v2 < 25 ??? 5;

 in this case 'a' evaluates to 5 because none of the conditions evaluate to
true except the last one. if you remove the last condition (??? 5) then 'a'
stays 0 in this case.

 If v2 = 23 then 'a' evaluates to 23;
 If v = 27 then 'a' evaluates to 27;

 etc.

 It means that it takes a sequential rvalue, tests it against a condition and
if the condition evaluates to true then it assigns that rvalue to lvalue. It
does not mean you are restricted to boolean values only.
I assume you meant v == 17, not v = 17 there, since v = 17 would be an assignment. In any case int a = v > 20 ??? v == 17 ??? v2 < 25 ??? 5; Looks to me the same as int a = v > 20 ? v : (v == 17 ? v : (v2 < 25 ? v2 : 5)); Could be just me, but the latter seems readable and more expressive.
Sep 23 2007
parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Janice Caron Wrote:

 On 9/23/07, Arlen Albert Keshabyan <arlen.albert gmail.com> wrote:
 Consider this:

 int v = 15;
 int v2 = 30;

 int a = v > 20 ??? v = 17 ??? v2 < 25 ??? 5;

 in this case 'a' evaluates to 5 because none of the conditions evaluate to
true except the last one. if you remove the last condition (??? 5) then 'a'
stays 0 in this case.

 If v2 = 23 then 'a' evaluates to 23;
 If v = 27 then 'a' evaluates to 27;

 etc.

 It means that it takes a sequential rvalue, tests it against a condition and
if the condition evaluates to true then it assigns that rvalue to lvalue. It
does not mean you are restricted to boolean values only.
I assume you meant v == 17, not v = 17 there, since v = 17 would be an assignment. In any case int a = v > 20 ??? v == 17 ??? v2 < 25 ??? 5; Looks to me the same as int a = v > 20 ? v : (v == 17 ? v : (v2 < 25 ? v2 : 5)); Could be just me, but the latter seems readable and more expressive.
Well, maybe you're right.
Sep 23 2007
prev sibling parent reply Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
and...

A a = APointer1 ?? APointer2 ?? APointer3;

the line above is equal to the line below:

A a = APointer1 != null ??? APointer2 != null ??? APointer3 != null ??? null;
Sep 23 2007
parent reply "Stewart Gordon" <smjg_1998 yahoo.com> writes:
"Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
news:fd68lm$f4r$1 digitalmars.com...
 and...

 A a = APointer1 ?? APointer2 ?? APointer3;

 the line above is equal to the line below:

 A a = APointer1 != null ??? APointer2 != null ??? APointer3 != null ??? 
 null;
Not by the way you specified it. The first would assign to a an actual object reference; the second would try to assign a boolean value. Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Sep 23 2007
parent reply Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Stewart Gordon Wrote:

 
 "Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
 news:fd68lm$f4r$1 digitalmars.com...
 and...

 A a = APointer1 ?? APointer2 ?? APointer3;

 the line above is equal to the line below:

 A a = APointer1 != null ??? APointer2 != null ??? APointer3 != null ??? 
 null;
Not by the way you specified it. The first would assign to a an actual object reference; the second would try to assign a boolean value. Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
No. The code lines do the same thing anyway.
Sep 23 2007
parent "Stewart Gordon" <smjg_1998 yahoo.com> writes:
"Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
news:fd6ee0$nd4$1 digitalmars.com...
 Stewart Gordon Wrote:
 "Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message
 news:fd68lm$f4r$1 digitalmars.com...
 A a = APointer1 ?? APointer2 ?? APointer3;

 the line above is equal to the line below:

 A a = APointer1 != null ??? APointer2 != null ??? APointer3 != null ???
 null;
Not by the way you specified it. The first would assign to a an actual object reference; the second would try to assign a boolean value.
<snip>
 No. The code lines do the same thing anyway.
Which same thing - evaluating to a boolean or evaluating to an object reference? Are you going to supply a corrected version of your proposal? Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Sep 23 2007
prev sibling next sibling parent reply "Stewart Gordon" <smjg_1998 yahoo.com> writes:
"Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message
news:fd611h$4co$1 digitalmars.com...
 It would be the sugar syntactic to add '??' operator to D.
 Consider the example code:

 string error_message = getErrorMessage() ??  "no errors";
 A a = x.getPreparedAObject() ??  y.getPreparedAObject() ??  new A();


 evaluates to the first non-null value or to null if all rvalues
 contains null.
So effectively, it works like || in JavaScript and the like. I guess the return type of a ?? expression would be determined by the same rules that govern that of a ConditionalExpression. The expression would evaluate to the first subexpression whose value when implicitly converted to a boolean is true. Otherwise ... to the .init of the return type?
 This operator might be extended to '???' to evaluate to a value
 that conforms to some conditions.  The lvalue gets the first rvalue
 that evaluates to true (evaluation goes from left to right).  If no
 conditions evaluates to true then the lvalue stays unchanged.  If
 no conditions are given explicitly then those conditions evaluates
 to true (so, the best place for them is at the end of a sequence).
<snip> I don't really like this: - It would cause the semantics of the = operator to depend on the form of the RHS. - What if the ??? expression isn't the RHS of an = operator? - The null case of this operator doesn't match semantically as they're normally expected to. (I'm not sure if there's any better way to word this.) To see what I mean, compare the meanings of a = b ??? c ??? d; a = b ??? c; a = b; Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Sep 23 2007
parent reply Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Stewart Gordon Wrote:

 "Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message
 news:fd611h$4co$1 digitalmars.com...
 It would be the sugar syntactic to add '??' operator to D.
 Consider the example code:

 string error_message = getErrorMessage() ??  "no errors";
 A a = x.getPreparedAObject() ??  y.getPreparedAObject() ??  new A();


 evaluates to the first non-null value or to null if all rvalues
 contains null.
So effectively, it works like || in JavaScript and the like. I guess the return type of a ?? expression would be determined by the same rules that govern that of a ConditionalExpression. The expression would evaluate to the first subexpression whose value when implicitly converted to a boolean is true. Otherwise ... to the .init of the return type?
 This operator might be extended to '???' to evaluate to a value
 that conforms to some conditions.  The lvalue gets the first rvalue
 that evaluates to true (evaluation goes from left to right).  If no
 conditions evaluates to true then the lvalue stays unchanged.  If
 no conditions are given explicitly then those conditions evaluates
 to true (so, the best place for them is at the end of a sequence).
<snip> I don't really like this: - It would cause the semantics of the = operator to depend on the form of the RHS. - What if the ??? expression isn't the RHS of an = operator? - The null case of this operator doesn't match semantically as they're normally expected to. (I'm not sure if there's any better way to word this.) To see what I mean, compare the meanings of a = b ??? c ??? d; a = b ??? c; a = b; Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
If you'd read my first post carefully you'd never ask the question like this.
 a = b ??? c ??? d;
 a = b ??? c;
 a = b;
the conditions must be explicit (!). Inexplicit conditions always evaluates to TRUE despite types involved in conditions. So, you will got the following:
 a = b ??? c ??? d;
a = b; //any type, even boolean
 a = b ??? c;
a = b;
 a = b;
a = b; //so no ??? ternary operator. Treated just like no (?:) operator :) bool a = true bool b = true bool c = false bool d = true; a = b != true ??? c != false ??? d == false ??? false; now a == false because no conditions evaluates to true except for the last one 'false'. Yes, 'false' evaluates to true :) so it's assigned to the 'a' variable.
Sep 24 2007
parent "Stewart Gordon" <smjg_1998 yahoo.com> writes:
"Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
news:fd7qu5$2t7e$1 digitalmars.com...
<snip>
 If you'd read my first post carefully you'd never ask the question like 
 this.

 a = b ??? c ??? d;
 a = b ??? c;
 a = b;
the conditions must be explicit (!). Inexplicit conditions always evaluates to TRUE despite types involved in conditions. So, you will got the following:
I meant the symbols to denote general expressions rather than variables. I must've blinked and missed the bit about the semantics of a ??? expression depending on the form of the operands. But anyway, what is the definition of an "explicit" or "inexplicit" condition"? Just whether the operator in the next level below the ??? happens to be == != is !is < <= > >= !<>= !<> <> <>= !> !>= !< !<= in? And if you're going to do this, why _allow_ "inexplicit" conditions anywhere except the end? ISTM it can only be a mistake, and one that's far too easy to make.
 a = b ??? c ??? d;
a = b; //any type, even boolean
 a = b ??? c;
a = b;
 a = b;
a = b; //so no ??? ternary operator. Treated just like no (?:) operator :)
<snip> You miss the point. Generally, when a binary operator is used n times in sequence, it denotes combining the n+1 operands in some fashion or another, which continues to hold when n == 0. For instance, x can be thought of as a sum of one value, just as x + y is a sum of two values, x + y + z is a sum of three values, and so on. Your ??? operator would break this principle: n = a == x ??? b == y ??? c == z; takes, of the three conditions, the first condition to be true and assigns it to n, otherwise leaves n unchanged n = a == x ??? b == y; takes, of the two conditions, the first condition to be true and assigns it to n, otherwise leaves n unchanged n = a == x; would by extension take, of the one condition, the first condition to be true and assign it to n, otherwise leave n unchanged. (In other words, assign the value of a == x to n only if a == x is true.) But it doesn't.... Whether this matters, I'm not sure. At least n = a == x ??? n; would do the equivalent for a single condition. But still.... Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Sep 24 2007
prev sibling next sibling parent reply renoX <renosky free.fr> writes:
Arlen Albert Keshabyan a écrit :
 It would be the sugar syntactic to add '??' operator to D. Consider
 the example code:
 
 string error_message = getErrorMessage() ?? "no errors"; A a =
 x.getPreparedAObject() ?? y.getPreparedAObject() ?? new A();
 

 evaluates to the first non-null value or to null if all rvalues
 contains null.[cut]
I feel as if the ?? operator is a workaround for the real issue, the fact that types reference can be null by default. In Nice by default, types are not nullable so you don't need to put null tests everywhere and there's a nice syntax for nullable type: just append a '?' to your type name i.e: Type x = ...; // Here you're sure that x can never be null. Type? x; // x may be null. This is a big change, but it feels "more right" to me. renoX
Sep 23 2007
parent Frank Benoit <keinfarbton googlemail.com> writes:
 
 Type x = ...; // Here you're sure that x can never be null.
 Type? x; // x may be null.
 
 
 This is a big change, but it feels "more right" to me.
 
 renoX
 
I second that.
Sep 23 2007
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 23 Sep 2007 11:34:09 -0400, Arlen Albert Keshabyan wrote:

 For instance:
 
 int codeval = getValue1() > 5 ??? getValue2() >= 4 ??? getValue3() != 0 ??? 1; 
How would this evaluate in "long hand" code? int a = b() > c() ??? d() >= e() ??? f() != g(); -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Sep 23 2007
next sibling parent reply "Stewart Gordon" <smjg_1998 yahoo.com> writes:
"Derek Parnell" <derek psych.ward> wrote in message 
news:1lomy00la8rsl.1fa6pft3qx480$.dlg 40tude.net...
<snip>
 How would this evaluate in "long hand" code?

 int a = b() > c() ??? d() >= e() ??? f() != g();
From what I can make out: int a = b() > c(); if (!a) a = (d() >= e()); if (!a) a = (f() != g()); This is slightly simpler than usual because it's an initialiser (and because int.init happens to be 0). But in the general case where it's being assigned after declaration, you'd need a bit more: int temp = b() > c(); if (!temp) temp = (d() >= e()); if (!temp) temp = (f() != g()); if (temp) a = temp; Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Sep 23 2007
parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Stewart Gordon Wrote:

 "Derek Parnell" <derek psych.ward> wrote in message 
 news:1lomy00la8rsl.1fa6pft3qx480$.dlg 40tude.net...
 <snip>
 How would this evaluate in "long hand" code?

 int a = b() > c() ??? d() >= e() ??? f() != g();
From what I can make out: int a = b() > c(); if (!a) a = (d() >= e()); if (!a) a = (f() != g()); This is slightly simpler than usual because it's an initialiser (and because int.init happens to be 0). But in the general case where it's being assigned after declaration, you'd need a bit more: int temp = b() > c(); if (!temp) temp = (d() >= e()); if (!temp) temp = (f() != g()); if (temp) a = temp; Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
int a = b() > c() ??? d() >= e() ??? f() != g(); can be represented like this: if(b() > c()) a = b(); else if(d() >= e()) a = d(); else if(f() != g()) a = f(); OR compiler must optimize it like this: { int temp = b(); if(temp > c()) a = temp; else { temp = d(); if(temp >= e()) a = temp; else { temp = f(); if(temp != g()) a = temp; } } }
Sep 24 2007
prev sibling parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Derek Parnell Wrote:

 On Sun, 23 Sep 2007 11:34:09 -0400, Arlen Albert Keshabyan wrote:
 
 For instance:
 
 int codeval = getValue1() > 5 ??? getValue2() >= 4 ??? getValue3() != 0 ??? 1; 
How would this evaluate in "long hand" code? int a = b() > c() ??? d() >= e() ??? f() != g(); -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
if(b() > c()) a = b(); else if(d() >= e()) a = d(); else if(f() != g()) a = f();
Sep 24 2007
prev sibling next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Arlen Albert Keshabyan Wrote:

 It would be the sugar syntactic to add '??' operator to D. Consider the
example code:
 
 string error_message = getErrorMessage() ?? "no errors";
 A a = x.getPreparedAObject() ?? y.getPreparedAObject() ?? new A();
 

the first non-null value or to null if all rvalues contains null. This operator
might be extended to '???' to evaluate to a value that conforms to some
conditions. The lvalue gets the first rvalue that evaluates to true (evaluation
goes from left to right). If no conditions evaluates to true then the lvalue
stays unchanged. If no conditions are given explicitly then those conditions
evaluates to true (so, the best place for them is at the end of a sequence).
 
 For instance:
 
 int codeval = getValue1() > 5 ??? getValue2() >= 4 ??? getValue3() != 0 ??? 1;
// if no conditions are satisfied then codeval = 1 as a default value at last.
There are no sane reasons to place it in the middle of the sequence.
 
 int code = 7;
 code = 5 < 10 ??? 0 != 0; // this way, code = 7 (no conditions are satisfied)
 
I have mixed feelings about the ternary operator. Nobody ever told me what it did, and I never saw it in any code when I was first learning to program, so consequently, when I finally saw it for the first time, I was baffled by it. Now that I understand it, I was using it quite often, but recently another developer (with 10 years more experience than I) was reviewing my code before a check-in and had never seen the conditional expression used outside a call expression/parameter list. Anyways, enough about my life. I think the ?? option should be added... I use that sort of thing in scripting languages all the time, and now that I know the dangers of the ?:, I might even use it more frequently than that. I don't think it should be limited just to object references; as nulls evaluate to false, it could be extended to other types with a logical false such as integers or boolean expressions: int x = 0 ?? 10; // Evaluates to 10 int y = 5 ?? 10; // Evaluates to 5 int z = 0 ?? 0; // Evaluates to 0 bool a = false ?? true; // Evaluates to true bool b = false ?? false; // Evaluates to false ??? seems very weird to me, though, so I'm against that one.
Sep 23 2007
next sibling parent Nathan Reed <nathaniel.reed gmail.com> writes:
Robert Fraser wrote:
 Anyways, enough about my life. I think the ?? option should be added... I use
that sort of thing in scripting languages all the time, and now that I know the
dangers of the ?:, I might even use it more frequently than that. I don't think
it should be limited just to object references; as nulls evaluate to false, it
could be extended to other types with a logical false such as integers or
boolean expressions:
 
 int x = 0 ?? 10; // Evaluates to 10
 int y = 5 ?? 10; // Evaluates to 5
 int z = 0 ?? 0; // Evaluates to 0
 bool a = false ?? true; // Evaluates to true
 bool b = false ?? false; // Evaluates to false
 
  ??? seems very weird to me, though, so I'm against that one.
Is there any reason that || couldn't be overloaded to do this? It does exactly the same thing except that the result is always a bool. a || b || c || ... The expressions are evaluated one by one starting from the left, and the first one that is true when implicitly converted to a bool is returned. If b is 'true' (meaning, it's a non-null reference, a non-zero number, etc), then c is never evaluated. If all the expressions are 'false' (null reference, 0, etc) then the last one is returned. Similiarly, we could define && such that a && b && c && ... results in the expressions being evaluated one by one starting from the left, and the first one that is 'false' is returned, or if they are all 'true', the last one is returned. If b is 'false', then c is never evaluated. This seems like it could be useful, wouldn't break existing code (as the results of these expressions would still be implicitly converted to bool when necessary), and don't introduce new operators into the language. Thanks, Nathan Reed
Sep 23 2007
prev sibling parent reply Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Robert Fraser Wrote:

 Arlen Albert Keshabyan Wrote:
 
 It would be the sugar syntactic to add '??' operator to D. Consider the
example code:
 
 string error_message = getErrorMessage() ?? "no errors";
 A a = x.getPreparedAObject() ?? y.getPreparedAObject() ?? new A();
 

the first non-null value or to null if all rvalues contains null. This operator
might be extended to '???' to evaluate to a value that conforms to some
conditions. The lvalue gets the first rvalue that evaluates to true (evaluation
goes from left to right). If no conditions evaluates to true then the lvalue
stays unchanged. If no conditions are given explicitly then those conditions
evaluates to true (so, the best place for them is at the end of a sequence).
 
 For instance:
 
 int codeval = getValue1() > 5 ??? getValue2() >= 4 ??? getValue3() != 0 ??? 1;
// if no conditions are satisfied then codeval = 1 as a default value at last.
There are no sane reasons to place it in the middle of the sequence.
 
 int code = 7;
 code = 5 < 10 ??? 0 != 0; // this way, code = 7 (no conditions are satisfied)
 
I have mixed feelings about the ternary operator. Nobody ever told me what it did, and I never saw it in any code when I was first learning to program, so consequently, when I finally saw it for the first time, I was baffled by it. Now that I understand it, I was using it quite often, but recently another developer (with 10 years more experience than I) was reviewing my code before a check-in and had never seen the conditional expression used outside a call expression/parameter list. Anyways, enough about my life. I think the ?? option should be added... I use that sort of thing in scripting languages all the time, and now that I know the dangers of the ?:, I might even use it more frequently than that. I don't think it should be limited just to object references; as nulls evaluate to false, it could be extended to other types with a logical false such as integers or boolean expressions: int x = 0 ?? 10; // Evaluates to 10 int y = 5 ?? 10; // Evaluates to 5 int z = 0 ?? 0; // Evaluates to 0 bool a = false ?? true; // Evaluates to true bool b = false ?? false; // Evaluates to false ??? seems very weird to me, though, so I'm against that one.
?? operator should handle only object references, I think. Nothing more than that. ??? ternary operator should handle any value types with condition expressions. The ?? operator should stop evaluating on the first null reference it encounters in a sequence. The ??? operator should stop evaluating on the first condition that evaluates to true EXPLICITLY. Any value in a sequence that has not explicit conditions should be treated like it evaluates to true despite any implicit conversions. So, the value of 0 should be evaluated to true in your case even if it might be implicitly converted to false.
Sep 24 2007
parent reply "Rioshin an'Harthen" <rharth75 hotmail.com> writes:
"Arlen Albert Keshabyan" <arlen.albert gmail.com> kirjoitti viestissä 
news:fd7rir$2u8a$1 digitalmars.com...
 ?? operator should handle only object references, I think. Nothing more 
 than that.
I can't begin to count how many times I've written code like long id = data.GetLong("ID") ?? 0; or long? id = data.GetLong("ID"); and then later accessing the value with id ?? 0 The ?? operator, as well as the nullable ? types, are quite nice to have when interfacing with the database.
Sep 24 2007
next sibling parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Rioshin anHarthen Wrote:

 "Arlen Albert Keshabyan" <arlen.albert gmail.com> kirjoitti viestissä 
 news:fd7rir$2u8a$1 digitalmars.com...
 ?? operator should handle only object references, I think. Nothing more 
 than that.
I can't begin to count how many times I've written code like long id = data.GetLong("ID") ?? 0; or long? id = data.GetLong("ID"); and then later accessing the value with id ?? 0 The ?? operator, as well as the nullable ? types, are quite nice to have when interfacing with the database.
Agreed partly.
Sep 24 2007
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/24/07, Rioshin an'Harthen <rharth75 hotmail.com> wrote:
 I can't begin to count how many times I've written code like

     long id = data.GetLong("ID") ?? 0;
make any sense of it. What is the return type of GetLong()? What is the type of the variable named data?
     long? id = data.GetLong("ID");
What does the question mark after long mean? What is the type of the variable named id? Is is "long" or "long?" ?
Sep 24 2007
parent reply "Rioshin an'Harthen" <rharth75 hotmail.com> writes:
"Janice Caron" <caron800 googlemail.com> kirjoitti viestissä 
news:mailman.289.1190665392.16939.digitalmars-d puremagic.com...
 On 9/24/07, Rioshin an'Harthen <rharth75 hotmail.com> wrote:
 I can't begin to count how many times I've written code like

     long id = data.GetLong("ID") ?? 0;
make any sense of it. What is the return type of GetLong()? What is the type of the variable named data?
Certainly. :) GetLong, as the name suggests, is used in this example to return a long (signed 64-bit value) from the database column ID, with the current database row referenced through the variable data (which, e.g. could be of type MySqlDataReader). GetLong may, if the column (in the row accessed) doesn't contain a value, return null, to signify this, since 0 (or any other value) may be a legal value in the database.
     long? id = data.GetLong("ID");
What does the question mark after long mean? What is the type of the variable named id? Is is "long" or "long?" ?
The question mark after a type name means the type is nullable: basically, in addition to its normal range may contain the value null. A nullable type can be built using a struct or class, and as far as I'm able to tell from If you wish further information on nullable types, I'd recommend reading http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf
Sep 25 2007
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/25/07, Rioshin an'Harthen <rharth75 hotmail.com> wrote:
 GetLong, as the name suggests, is used in this example to return a long
 (signed 64-bit value) from the database column ID, with the current database
 row referenced through the variable data (which, e.g. could be of type
 MySqlDataReader). GetLong may, if the column (in the row accessed) doesn't
 contain a value, return null, to signify this, since 0 (or any other value)
 may be a legal value in the database.
You contradict yourself. Clearly it does /not/ return a ulong, since a ulong cannot contain null (or null is indistinguishable from zero, depending on your point of view). So what you really mean is that the type ulong? (with a question mark) is basically equivalent to struct { ulong n; bool isNull; } which implicitly casts to its n member, and that ?? tests the isNull field. I'm sure this could be done with templates. That is: Nullable!(long) id = data.GetNullableLong("ID"); if (isNulled(id)) { /* ... */ } long n = id; // implicit cast Or, all in one step, your long id = data.GetLong("ID") ?? 0; would become long id = isNulled(auto temp = data.GetLong("ID")) ? 0 : temp;
Sep 25 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Of course, the /easy/ way to do this is to have GetLong return a long
*, and then do

long * temp;
long id = (temp = data.getLong("ID")) ? *temp : 0;
Sep 25 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/25/07, Janice Caron <caron800 googlemail.com> wrote:
 Of course, the /easy/ way to do this is to have GetLong return a long
 *, and then do

 long * temp;
 long id = (temp = data.getLong("ID")) ? *temp : 0;
Gosh, that word-wrapped in an inconvenient place! I meant that GetLong should return a pointer to long.
Sep 25 2007
prev sibling next sibling parent reply Max Samukha <samukha voliacable.com.removethis> writes:
On Tue, 25 Sep 2007 11:39:51 +0300, "Rioshin an'Harthen"
<rharth75 hotmail.com> wrote:

"Janice Caron" <caron800 googlemail.com> kirjoitti viestiss? 
news:mailman.289.1190665392.16939.digitalmars-d puremagic.com...
 On 9/24/07, Rioshin an'Harthen <rharth75 hotmail.com> wrote:
 I can't begin to count how many times I've written code like

     long id = data.GetLong("ID") ?? 0;
make any sense of it. What is the return type of GetLong()? What is the type of the variable named data?
Certainly. :) GetLong, as the name suggests, is used in this example to return a long (signed 64-bit value) from the database column ID, with the current database row referenced through the variable data (which, e.g. could be of type MySqlDataReader). GetLong may, if the column (in the row accessed) doesn't contain a value, return null, to signify this, since 0 (or any other value) may be a legal value in the database.
     long? id = data.GetLong("ID");
What does the question mark after long mean? What is the type of the variable named id? Is is "long" or "long?" ?
The question mark after a type name means the type is nullable: basically, in addition to its normal range may contain the value null. A nullable type can be built using a struct or class, and as far as I'm able to tell from If you wish further information on nullable types, I'd recommend reading http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf
additional bool member. ? is just a syntactic sugar for the templated System.Nullable struct. Here is a quickly hacked together example of how nullable types could be implemented in D using templates. struct Nullable(T) { alias typeof(*this) Type; T value; bool isNull = true; Type opAssign(T v) { value = v; isNull = false; return *this; } static Type opCall(T v) { Type ret; ret.value = v; ret.isNull = false; return ret; } /* Hacks to allow null keyword in assignments/comparisons if you really want it */ Type opAssign(void* v) { assert(v == null); isNull = true; return *this; } int opEquals(void* v) { assert(v == null); return isNull; } T getValueOrDefault() { return isNull ? T.init : value; } T ifNull(T v) { return isNull ? v : value; } /* etc. opImplicitCasts and struct interfaces will be useful here */ } class DataReader { Nullable!(T) get(T)(char[] field) { Nullable!(T) ret; return ret; } } void main() { auto data = new DataReader; auto id = data.get!(long)("id"); assert(id == null); id = 20; assert(id != null); id = null; // a replacement for 'long x = id ?? -1' long x = id.ifNull(-1); assert (x == -1); } I'm sure DB people already use something like that.
Sep 25 2007
parent reply Reiner Pope <some address.com> writes:
Max Samukha wrote:
 On Tue, 25 Sep 2007 11:39:51 +0300, "Rioshin an'Harthen"
 <rharth75 hotmail.com> wrote:
 
 "Janice Caron" <caron800 googlemail.com> kirjoitti viestiss? 
 news:mailman.289.1190665392.16939.digitalmars-d puremagic.com...
 On 9/24/07, Rioshin an'Harthen <rharth75 hotmail.com> wrote:
 I can't begin to count how many times I've written code like

     long id = data.GetLong("ID") ?? 0;
make any sense of it. What is the return type of GetLong()? What is the type of the variable named data?
Certainly. :) GetLong, as the name suggests, is used in this example to return a long (signed 64-bit value) from the database column ID, with the current database row referenced through the variable data (which, e.g. could be of type MySqlDataReader). GetLong may, if the column (in the row accessed) doesn't contain a value, return null, to signify this, since 0 (or any other value) may be a legal value in the database.
     long? id = data.GetLong("ID");
What does the question mark after long mean? What is the type of the variable named id? Is is "long" or "long?" ?
The question mark after a type name means the type is nullable: basically, in addition to its normal range may contain the value null. A nullable type can be built using a struct or class, and as far as I'm able to tell from If you wish further information on nullable types, I'd recommend reading http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf
additional bool member. ? is just a syntactic sugar for the templated System.Nullable struct. Here is a quickly hacked together example of how nullable types could be implemented in D using templates. struct Nullable(T) { alias typeof(*this) Type; T value; bool isNull = true; Type opAssign(T v) { value = v; isNull = false; return *this; } static Type opCall(T v) { Type ret; ret.value = v; ret.isNull = false; return ret; } /* Hacks to allow null keyword in assignments/comparisons if you really want it */ Type opAssign(void* v) { assert(v == null); isNull = true; return *this; } int opEquals(void* v) { assert(v == null); return isNull; } T getValueOrDefault() { return isNull ? T.init : value; } T ifNull(T v) { return isNull ? v : value; } /* etc. opImplicitCasts and struct interfaces will be useful here */ } class DataReader { Nullable!(T) get(T)(char[] field) { Nullable!(T) ret; return ret; } } void main() { auto data = new DataReader; auto id = data.get!(long)("id"); assert(id == null); id = 20; assert(id != null); id = null; // a replacement for 'long x = id ?? -1' long x = id.ifNull(-1); assert (x == -1); } I'm sure DB people already use something like that.
Looks good. And we can overload the bitwise OR operator to make it look a little better (in my opinion). We just add Nullable!(T) opOr(Nullable!(T) other) { return isNull ? other : Type(value); } T opOr(T other) { return isNull ? other : value; } to your struct, and then as a new main, we get void main() { auto data = new DataReader; auto id = data.get!(long)("id"); assert(id == null); auto id2 = data.get!(long)("id2"); id2 = 25; auto y = id | id2; // y is a Nullable!(T) assert(y != null); long x = y | -1; assert (x == 25); id = 20; assert(id | id2 | -1 == 20); } Voila! Unfortunately, I couldn't get short-circuiting to work via using lazy on parameters types to opOr. This is because | is parsed left-associatively, so that doesn't work. I wonder if some expression-templatey work could fix this... -- Reiner
Sep 25 2007
parent reply Max Samukha <samukha voliacable.com.removethis> writes:
On Tue, 25 Sep 2007 22:33:15 +1000, Reiner Pope <some address.com>
wrote:

Looks good. And we can overload the bitwise OR operator to make it look 
a little better (in my opinion). We just add

         Nullable!(T) opOr(Nullable!(T) other)
         {
             return isNull ? other : Type(value);
         }

         T opOr(T other)
         {
             return isNull ? other : value;
         }

to your struct, and then as a new main, we get

void main()
{
     auto data = new DataReader;

     auto id = data.get!(long)("id");
     assert(id == null);

     auto id2 = data.get!(long)("id2");
     id2 = 25;

     auto y = id | id2; // y is a Nullable!(T)
     assert(y != null);

     long x = y | -1;
     assert (x == 25);

     id = 20;
     assert(id | id2 | -1 == 20);
}


Voila!

Unfortunately, I couldn't get short-circuiting to work via using lazy on 
parameters types to opOr. This is because | is parsed 
left-associatively, so that doesn't work. I wonder if some 
expression-templatey work could fix this...

     -- Reiner
I like the way it looks but changing the meaning of overloaded operators is not recommended by the dogmata. It seems like ?? operator would be a useful addition to the language.
Sep 25 2007
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
I think I've solved the problem. This works!


    int main()
    {
        int[string] aa;
        aa["B"] = 42;

        int n = firstOf("A" in aa)("B" in aa)("C" in aa)(0);
        writefln(n);

        return 0;
    }

As you'd expect, it prints 42. But wait - there's more! Just to prove
that it's not doing unnecessary work, watch this:

    int * lookup(string s, int[string] aa)
    {
        writef("(%s) ",s);
        return s in aa;
    }

    int main()
    {
        int[string] aa;
        aa["B"] = 42;

        int m = firstOf(lookup("A",aa))(lookup("B",aa))(lookup("C",aa))(0);
        writefln(m);

        return 0;
    }

This prints
(A) (B) 42.

So the third lookup is not done. Woo hoo! And all with no ?? operator.
How's it done? Well, I'll tell you. It's done like this:


    SnazzyFunctor firstOf(lazy int * p)
    {
        return new SnazzyFunctor(p);
    }

    class SnazzyFunctor
    {
        this(int * p)
        {
            result = p;
        }

        SnazzyFunctor opCall(lazy int * p)
        {
            if (result == null) result = p;
            return firstOf(result);
        }

        int opCall(lazy int n)
        {
            if (result != null) return *result;
            return n;
        }

        int * result;
    }

(I'm sure worthier minds than mine could templatise this)
Sep 26 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/26/07, Janice Caron <caron800 googlemail.com> wrote:
     SnazzyFunctor firstOf(lazy int * p)
     {
         return new SnazzyFunctor(p);
     }
Come to think of it, it should work with one fewer lazy: SnazzyFunctor firstOf(int * p) { return new SnazzyFunctor(p); } since you're always going to want the first one to execute, and the recursive call has already been evaluated. So, even simpler then!
Sep 26 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/25/07, Janice Caron <caron800 googlemail.com> wrote:
 Of course, the /easy/ way to do this is to have GetLong return a
 long *, and then do

 long * temp;
 long id = (temp = data.getLong("ID")) ? *temp : 0;
Or chaining lots of tests together long id = *( get("A") || get("B") || get("C") || get("D") || &default );
Sep 25 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
 Or chaining lots of tests together

 long id = *( get("A") || get("B") || get("C") || get("D") || &default );
Oh wait! That won't work, because you can't overload ||. Still, it was good try. I think it would be kinda cool if || could be overloaded to accept pointer inputs. That is, if the LHS and RHS were both of type T* then the expression (lhs || rhs) could also have type T*, and would mean ((lhs !is null) ? lhs : rhs). That would be much nicer than introducing new operators. (It's still a change to the language though).
Sep 25 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
How about this..

T maybe(T)(T * ptr, lazy T default)
{
	if (ptr !is null) return *ptr;
	return default;
}

long p = maybe(get("A"), maybe(get("B"), maybe(get("C"),
maybe(get("D"), maybe(get("E"), 0 )))));
Sep 25 2007
prev sibling next sibling parent Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Stewart Gordon Wrote:

 "Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
 news:fd6ee0$nd4$1 digitalmars.com...
 Stewart Gordon Wrote:
 "Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message
 news:fd68lm$f4r$1 digitalmars.com...
 A a = APointer1 ?? APointer2 ?? APointer3;

 the line above is equal to the line below:

 A a = APointer1 != null ??? APointer2 != null ??? APointer3 != null ???
 null;
Not by the way you specified it. The first would assign to a an actual object reference; the second would try to assign a boolean value.
<snip>
 No. The code lines do the same thing anyway.
Which same thing - evaluating to a boolean or evaluating to an object reference? Are you going to supply a corrected version of your proposal? Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
It evaluates to an object reference.
Sep 24 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Arlen Albert Keshabyan wrote:
 It would be the sugar syntactic to add '??' operator to D. Consider the
example code:
 
 string error_message = getErrorMessage() ?? "no errors";
 A a = x.getPreparedAObject() ?? y.getPreparedAObject() ?? new A();
 

evaluates to the first non-null value or to null if all rvalues contains null. This operator might be extended to '???' to evaluate to a value that conforms to some conditions. The lvalue gets the first rvalue that evaluates to true (evaluation goes from left to right). If no conditions evaluates to true then the lvalue stays unchanged. If no conditions are given explicitly then those conditions evaluates to true (so, the best place for them is at the end of a sequence).
 
 For instance:
 
 int codeval = getValue1() > 5 ??? getValue2() >= 4 ??? getValue3() != 0 ??? 1;
// if no conditions are satisfied then codeval = 1 as a default value at last.
There are no sane reasons to place it in the middle of the sequence.
 
 int code = 7;
 code = 5 < 10 ??? 0 != 0; // this way, code = 7 (no conditions are satisfied)
 
It would be great to have a way to express complicated_expression > 0 ? complicated_expression : default Without having to duplicate complicated_expression, while still remaining an expression overall. But I find the "a op b ??? default" syntax illogical. It looks like 'a op b' should be evaluated, becoming a boolean. How about Something like: complicated_expression ?> 0 : -1 evaluates to "complicated_expression if it's greater than zero, otherwise -1" [Of course there are a dozen other bugs and features I'd rather see handled before this ;-)] --bb
Sep 24 2007
parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/24/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 It would be great to have a way to express

    complicated_expression > 0 ? complicated_expression : default

 Without having to duplicate complicated_expression, while still
 remaining an expression overall.
(x = complicated_expression) > 0 ? x : default You'd have to declare x first, but I don't see that as a major problem, and it does seem more sensible to me than creating new operators. (If there's one thing worse than keyword bloat, it's operator bloat).
Sep 24 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 9/24/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 It would be great to have a way to express

    complicated_expression > 0 ? complicated_expression : default

 Without having to duplicate complicated_expression, while still
 remaining an expression overall.
(x = complicated_expression) > 0 ? x : default You'd have to declare x first, but I don't see that as a major problem,
It *is* a problem in a template where you can't use the name-matches-return-value trick if there's any extraneous stuff defined in the template. Like so template ComputeAConstant(Foo) { const ComputeAConstant = complicated_expression(Foo) > 0 ? complicated_expression(Foo) : default; } if you stick an x in there you can no longer use it as just ComputeAConstant(float). Of course that should just be fixed anyway, but it's also not so nice to have to declare something first in other situations. Like switch cases or nested if-elseifs.
 and it does seem more sensible to me than creating new
 operators. (If there's one thing worse than keyword bloat, it's
 operator bloat).
At least these operators (?> ?< ?==) wouldn't require any new opBlah overloads. --bb
Sep 24 2007
prev sibling parent Alexander Panek <a.panek brainsware.org> writes:
Arlen Albert Keshabyan wrote:
 It would be the sugar syntactic to add '??' operator to D. Consider the
example code:
 
 string error_message = getErrorMessage() ?? "no errors";
 A a = x.getPreparedAObject() ?? y.getPreparedAObject() ?? new A();
 

the first non-null value or to null if all rvalues contains null. This operator
might be extended to '???' to evaluate to a value that conforms to some
conditions. The lvalue gets the first rvalue that evaluates to true (evaluation
goes from left to right). If no conditions evaluates to true then the lvalue
stays unchanged. If no conditions are given explicitly then those conditions
evaluates to true (so, the best place for them is at the end of a sequence).
 
 For instance:
 
 int codeval = getValue1() > 5 ??? getValue2() >= 4 ??? getValue3() != 0 ??? 1;
// if no conditions are satisfied then codeval = 1 as a default value at last.
There are no sane reasons to place it in the middle of the sequence.
 
 int code = 7;
 code = 5 < 10 ??? 0 != 0; // this way, code = 7 (no conditions are satisfied)
 
if (auto result_of_complex_expression = complex_expression_or_function()) var = result_of_complex_expression; // else var = null;
Sep 24 2007