www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Project Elvis

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter and I decided to kick-off project Elvis for adding the homonym 
operator to D.

Razvan Nitu has already done a good part of the work:

https://github.com/dlang/dmd/pull/7242
https://github.com/dlang/dlang.org/pull/1917
https://github.com/dlang/dlang.org/pull/1918

What's needed is a precise DIP that motivates the feature properly and 
provides a good proposal for it. I'm no fan of bureaucracy but we really 
need to be pedantic about introducing language features. Walter argued 
thusly in a PR, and I agree:

"I'm concerned that the elvis operator is not well understood, and we 
shouldn't be designing it in the comments section here. A DIP needs to 
be written. Things like operator precedence, side effects, type 
resolution, comparison with the operator in other languages, grammar 
changes, lvalues, how it would appear in the generated .di file if it 
isn't its own operator, etc., should be addressed."

A lowering looks like the straightforward approach, of the kind:

expr1 ?: expr2

==>

(x => x ? x : expr2)(expr1)

Who wants to join Razvan in Project Elvis?


Thanks,

Andrei
Oct 28
next sibling parent reply bauss <jj_1337 live.dk> writes:
On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu 
wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym operator to D.

 [...]
This is honestly going to be a great addition.
Oct 28
parent Daniel Kozak <kozzi11 gmail.com> writes:
Wait? You are saying D does not support this yet? Wow :D. I have been using
this so often in work (PHP) so I can beleive I have not miss this

On Sat, Oct 28, 2017 at 2:39 PM, bauss via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu wrote:

 Walter and I decided to kick-off project Elvis for adding the homonym
 operator to D.

 [...]
This is honestly going to be a great addition.
Oct 28
prev sibling next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu 
wrote:
 expr1 ?: expr2
So, what will the member function be called? «opElvis»? No…
Oct 29
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On Sunday, 29 October 2017 at 10:08:41 UTC, Ola Fosheim Grøstad 
wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei 
 Alexandrescu wrote:
 expr1 ?: expr2
So, what will the member function be called? «opElvis»? No…
opCast for bool. -Steve
Oct 29
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 29 October 2017 at 11:23:19 UTC, Steven Schveighoffer 
wrote:
 So, what will the member function be called? «opElvis»? No…
opCast for bool.
That means you cannot create your own type-safe filtering mechanism, but will be forced to provide opCast for bool.
Oct 29
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 29 October 2017 at 14:27:34 UTC, Ola Fosheim Grøstad 
wrote:
 On Sunday, 29 October 2017 at 11:23:19 UTC, Steven 
 Schveighoffer wrote:
 So, what will the member function be called? «opElvis»? No…
opCast for bool.
That means you cannot create your own type-safe filtering mechanism, but will be forced to provide opCast for bool.
That breaks down if you want do filter out invalid values where zero is a valid value. For instance if you have an integer type that tracks overflow: calc(maybe_overflow_integer :? 0) So casting to bool is a poor choice for the typical use context.
Oct 29
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/29/17 10:35 AM, Ola Fosheim Grøstad wrote:
 On Sunday, 29 October 2017 at 14:27:34 UTC, Ola Fosheim Grøstad wrote:
 On Sunday, 29 October 2017 at 11:23:19 UTC, Steven Schveighoffer wrote:
 So, what will the member function be called? «opElvis»? No…
opCast for bool.
That means you cannot create your own type-safe filtering mechanism, but will be forced to provide opCast for bool.
That breaks down if you want do filter out invalid values where zero is a valid value. For instance if you have an integer type that tracks overflow: calc(maybe_overflow_integer :? 0) So casting to bool is a poor choice for the typical use context.
I would have expected Nullable!int to fit the bill, but unfortunately, opCast(bool) doesn't work on a null Nullable. But certainly you can construct a type that does work. -Steve
Oct 29
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 29 October 2017 at 15:57:19 UTC, Steven Schveighoffer 
wrote:
 I would have expected Nullable!int to fit the bill, but 
 unfortunately, opCast(bool) doesn't work on a null Nullable.

 But certainly you can construct a type that does work.
The right thing to do is to create a type that you cannot cast to bool, but where you can test for invalid values and substitute in a default. Forcing people to have a boolean interpretation of a type mean valid/invalid state has viral consequences. It means that if the type has a zero value then a boolean interpretation of zero now should give true. That's not good for generic code. Float and NaN is another use case.
Oct 29
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/29/17 12:29 PM, Ola Fosheim Grøstad wrote:
 On Sunday, 29 October 2017 at 15:57:19 UTC, Steven Schveighoffer wrote:
 I would have expected Nullable!int to fit the bill, but unfortunately, 
 opCast(bool) doesn't work on a null Nullable.

 But certainly you can construct a type that does work.
The right thing to do is to create a type that you cannot cast to bool, but where you can test for invalid values and substitute in a default.
This is pretty simple, the if(x) provides a mechanism to check called "casting to bool". That doesn't mean it will shoehorn bool into the expression. In fact, the elvis operator provides a perfect way to type the result with the "common type" rules of D. The way to do it is to make a type that checks whether the expression is valid or not, makes that into a bool, and then provides the real expression to the result.
 Forcing people to have a boolean interpretation of a type mean 
 valid/invalid state has viral consequences. It means that if the type 
 has a zero value then a boolean interpretation of zero now should give 
 true. That's not good for generic code.
It's actually perfect for generic code. If you need something other than the current "0 means false" behavior (like for instance int), then you wrap in a type that opCast!bool means what you want. It should work just like a pointer. In swift this is exactly what the ? operator does. I just wish Nullable worked this way, I'm surprised it doesn't. -Steve
Oct 29
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 29 October 2017 at 20:05:08 UTC, Steven Schveighoffer 
wrote:
 It's actually perfect for generic code. If you need something 
 other than the current "0 means false" behavior (like for 
 instance int), then you wrap in a type that opCast!bool means 
 what you want.
I think we just have to agree that we disagree. Generic programming relies on consistent protocols. So, you generally don't want 0 to be considered as an invalid value. Because of the defaults in D, cast(bool) isn't really all that useful. It would have been better if the default was to deny casting to bool, but that is too late as D has decided to be too close to C on so many levels, so it would be a bad idea to diverge from C for that now. So the next best thing is to let the programmer specify that something is invalid with some other means than opCast to bool. *shrug*
Oct 29
parent reply bauss <jj_1337 live.dk> writes:
On Sunday, 29 October 2017 at 20:15:41 UTC, Ola Fosheim Grøstad 
wrote:
 On Sunday, 29 October 2017 at 20:05:08 UTC, Steven 
 Schveighoffer wrote:
 It's actually perfect for generic code. If you need something 
 other than the current "0 means false" behavior (like for 
 instance int), then you wrap in a type that opCast!bool means 
 what you want.
I think we just have to agree that we disagree. Generic programming relies on consistent protocols. So, you generally don't want 0 to be considered as an invalid value. Because of the defaults in D, cast(bool) isn't really all that useful. It would have been better if the default was to deny casting to bool, but that is too late as D has decided to be too close to C on so many levels, so it would be a bad idea to diverge from C for that now. So the next best thing is to let the programmer specify that something is invalid with some other means than opCast to bool. *shrug*
But casting to bool is what you use to tell whether something is valid or not. true = valid, false = invalid. If you want 0 to be valid for a type then you wrap around it with opCast. Ex. --- import std.stdio; struct MyInt { int value; bool opCast(T : bool)() { return value >= 0; } } void main() { MyInt a = MyInt(1); MyInt b = MyInt(0); MyInt c = MyInt(-1); if (a) writeln("a is valid"); if (b) writeln("b is valid"); if (c) writeln("c is valid"); } --- Output: a is valid b is valid
Oct 29
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 29 October 2017 at 20:37:21 UTC, bauss wrote:
 But casting to bool is what you use to tell whether something 
 is valid or not.

 true = valid, false = invalid.
Not really. In mathematics 0 and 1 can be considered as "true" and "false" for a 2-value calculus, while you might reserve ⊤ and ⊥ for true and false in the logic you use to reason about that calculus. Which is why some languages assumes an equality between 0 and true and 1 and false, but that does not by necessity suggest valid/invalid. On the other hand. For things like Unix function call return values -1 is often used to signify an invalid result, and 0 does not signify failure. So if you want strict typing, you have to do something else. Because C (and thus D) takes the mathematical view on the relationship between integers and bools and propagate that view to all other basic types (e.g. floats). Ola.
Oct 29
parent reply w0rp <devw0rp gmail.com> writes:
One man's valid value is another man's invalid value. You can't 
test for a general concept of "invalid," as you need context. You 
can test for "falsy" with no context.
Oct 29
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 30 October 2017 at 00:10:13 UTC, w0rp wrote:
 One man's valid value is another man's invalid value. You can't 
 test for a general concept of "invalid," as you need context. 
 You can test for "falsy" with no context.
No, associating the numeral "0" with "false" is forcing a particular interpretation of int as representing a count of something. That is not an inate quality of the integer programming language type as such. For instance, there is no reason for "0 fahrenheit" to be "false". Only if "0" represents the "empty set" would that interpretation make some sense. Yes, you can obviously have a general concept of invalid in a strict typing system.
Oct 29
prev sibling parent Satoshi <satoshi rikarin.org> writes:
On Sunday, 29 October 2017 at 20:37:21 UTC, bauss wrote:
 On Sunday, 29 October 2017 at 20:15:41 UTC, Ola Fosheim Grøstad 
 wrote:
 [...]
But casting to bool is what you use to tell whether something is valid or not. true = valid, false = invalid. If you want 0 to be valid for a type then you wrap around it with opCast. Ex. --- import std.stdio; struct MyInt { int value; bool opCast(T : bool)() { return value >= 0; } } void main() { MyInt a = MyInt(1); MyInt b = MyInt(0); MyInt c = MyInt(-1); if (a) writeln("a is valid"); if (b) writeln("b is valid"); if (c) writeln("c is valid"); } --- Output: a is valid b is valid
TL;DR This could be done by maybe monad. int? a = 0; if (a) writeln("a is valid"); BTW: Thanks for implementing the Elvis feature.
Oct 30
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, October 29, 2017 14:27:34 Ola Fosheim Grøstad via Digitalmars-d 
wrote:
 On Sunday, 29 October 2017 at 11:23:19 UTC, Steven Schveighoffer

 wrote:
 So, what will the member function be called? «opElvis»? No…
opCast for bool.
That means you cannot create your own type-safe filtering mechanism, but will be forced to provide opCast for bool.
opCast with bool is precisely how you already provide overloads for dealing with every other place in the language that a boolean condition is used - the ternary operator, assertions, if statements, while loops, and for loops (which I think is everything, but I could have missed something). As proposed thus far, the Elvis operator is just the ternary operator where the condition is reused as the first branch, so having it work with opCast fits in perfectly with everything else. What would you be looking to do that does not fit in with this? - Jonathan M Davis
Oct 29
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 29 October 2017 at 14:37:57 UTC, Jonathan M Davis 
wrote:
 everything, but I could have missed something). As proposed 
 thus far, the Elvis operator is just the ternary operator where 
 the condition is reused as the first branch, so having it work 
 with opCast fits in perfectly with everything else.
I understand that it is being defined as a short hand for the ordinary ternary operator, but if you look at the use context the elvis-operator tend to be used to filter out invalid state. So if casting to bool would test for validity then it would make more sense, but that is not the general case...
 What would you be looking to do that does not fit in with this?
I think it would be better to have a test for validity and use that for substituting in default values (which is what the elvis operator is typically used for).
Oct 29
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, October 29, 2017 16:18:43 Ola Fosheim Grstad via Digitalmars-d 
wrote:
 On Sunday, 29 October 2017 at 14:37:57 UTC, Jonathan M Davis

 wrote:
 everything, but I could have missed something). As proposed
 thus far, the Elvis operator is just the ternary operator where
 the condition is reused as the first branch, so having it work
 with opCast fits in perfectly with everything else.
I understand that it is being defined as a short hand for the ordinary ternary operator, but if you look at the use context the elvis-operator tend to be used to filter out invalid state. So if casting to bool would test for validity then it would make more sense, but that is not the general case...
 What would you be looking to do that does not fit in with this?
I think it would be better to have a test for validity and use that for substituting in default values (which is what the elvis operator is typically used for).
And what does testing for validity even mean? Doesn't that depend on the type? I would argue that with regards to the built-in types what cast(bool) does for them is as close to checking for validity as you're going to get, and for user-defined types, they can then just overload opCast!bool to mean whatever they want so long as the result is true or false. In general, if you're looking to check whether something is valid using ?:, I would think that you'd want to be doing the same check with stuff like if statements anyway. So, it sounds to me like overloading opCast!bool would work just fine. - Jonathan M Davis
Oct 29
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 29 October 2017 at 16:29:57 UTC, Jonathan M Davis 
wrote:
 valid using ?:, I would think that you'd want to be doing the 
 same check with stuff like if statements anyway. So, it sounds 
 to me like overloading opCast!bool would work just fine.
If you try to do: some_float ?: 0.0 then it will do nothing as cast(bool)std.math.NaN(0) => true But that is just one of many possible examples. The elvis operator as proposed does not match on nullity / invalid state.
Oct 29
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, October 29, 2017 16:44:39 Ola Fosheim Grstad via Digitalmars-d 
wrote:
 On Sunday, 29 October 2017 at 16:29:57 UTC, Jonathan M Davis

 wrote:
 valid using ?:, I would think that you'd want to be doing the
 same check with stuff like if statements anyway. So, it sounds
 to me like overloading opCast!bool would work just fine.
If you try to do: some_float ?: 0.0 then it will do nothing as cast(bool)std.math.NaN(0) => true
NaN is supposed to always be false.
 But that is just one of many possible examples. The elvis
 operator as proposed does not match on nullity / invalid state.
Well, the closest that the language has to that is cast(bool). There is nothing else generic for anything along those lines. If you want something else, then just use the ternary operator with whatever condition you want to be testing. It works just fine and isn't much longer. Personally, I don't think that the Elvis operator is worth much - at least not in D. Idiomatic D code doesn't use classes much. Dynamic arrays largely conflate null with empty. Very little does anything with null - especially if you're writing range-based code - and I've rarely seen code where x ? x : y was used. The closest that I've typically seen or done to x ? x : y is if(auto var = foo()) { } and that's not useful for much beyond dealing with functions that return error codes or getting values from associative arrays. And it's not like using the ternary operator is very verbose. But the whole idea of "check if this is valid, if it is use it, and if it isn't, use a default" simply isn't an idiom that I use much, and I don't think that it's very common in Phobos. I also tend to prefer being explicit about conditions, so I don't typically rely on things like cast(bool) on types, and that's exactly the sort of thing that the Elvis operator would rely on whether it used cast(bool) or it was overloaded as its own operator like you seem to want. So, I really don't think that there's any point in adding the Elvis operator, but there are some folks here who seem to think that it's a great loss that we don't have it, because they're used to writing stuff like that in languages like C#, which do way more with classes and null than D code typically does. - Jonathan M Davis
Oct 29
parent reply Nemanja Boric <4burgos gmail.com> writes:
On Sunday, 29 October 2017 at 17:19:44 UTC, Jonathan M Davis 
wrote:
 On Sunday, October 29, 2017 16:44:39 Ola Fosheim Grøstad via 
 Digitalmars-d wrote:
 On Sunday, 29 October 2017 at 16:29:57 UTC, Jonathan M Davis

 wrote:
 valid using ?:, I would think that you'd want to be doing 
 the same check with stuff like if statements anyway. So, it 
 sounds to me like overloading opCast!bool would work just 
 fine.
If you try to do: some_float ?: 0.0 then it will do nothing as cast(bool)std.math.NaN(0) => true
NaN is supposed to always be false.
OT, but I had to :-) ``` void main() { import std.stdio; float x; x? writeln("Oh no, a NaN!") : writeln("All good."); } ``` Same happens for assert(float.nan) - it doesn't fail.
Oct 29
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, October 29, 2017 17:35:25 Nemanja Boric via Digitalmars-d wrote:
 On Sunday, 29 October 2017 at 17:19:44 UTC, Jonathan M Davis

 wrote:
 On Sunday, October 29, 2017 16:44:39 Ola Fosheim Grstad via

 Digitalmars-d wrote:
 On Sunday, 29 October 2017 at 16:29:57 UTC, Jonathan M Davis

 wrote:
 valid using ?:, I would think that you'd want to be doing
 the same check with stuff like if statements anyway. So, it
 sounds to me like overloading opCast!bool would work just
 fine.
If you try to do: some_float ?: 0.0 then it will do nothing as cast(bool)std.math.NaN(0) => true
NaN is supposed to always be false.
OT, but I had to :-) ``` void main() { import std.stdio; float x; x? writeln("Oh no, a NaN!") : writeln("All good."); } ``` Same happens for assert(float.nan) - it doesn't fail.
Sounds like a bug to me. NaN is supposed to be false whenever it's used in a comparison. If it it's true when cast directly to bool, then that's inconsistent. - Jonathan M Davis
Oct 29
next sibling parent reply Nemanja Boric <4burgos gmail.com> writes:
On Sunday, 29 October 2017 at 18:02:25 UTC, Jonathan M Davis 
wrote:
 On Sunday, October 29, 2017 17:35:25 Nemanja Boric via 
 Digitalmars-d wrote:
 On Sunday, 29 October 2017 at 17:19:44 UTC, Jonathan M Davis

 wrote:
 On Sunday, October 29, 2017 16:44:39 Ola Fosheim Grøstad via

 Digitalmars-d wrote:
 On Sunday, 29 October 2017 at 16:29:57 UTC, Jonathan M Davis

 wrote:
 valid using ?:, I would think that you'd want to be doing 
 the same check with stuff like if statements anyway. So, 
 it sounds to me like overloading opCast!bool would work 
 just fine.
If you try to do: some_float ?: 0.0 then it will do nothing as cast(bool)std.math.NaN(0) => true
NaN is supposed to always be false.
OT, but I had to :-) ``` void main() { import std.stdio; float x; x? writeln("Oh no, a NaN!") : writeln("All good."); } ``` Same happens for assert(float.nan) - it doesn't fail.
Sounds like a bug to me. NaN is supposed to be false whenever it's used in a comparison. If it it's true when cast directly to bool, then that's inconsistent. - Jonathan M Davis
We've already reported this as a bug (I actually got quite burned on it, trusting assert(float_value) to prevent NaN's escaping the function), but there were different opinions on this, so it never got anywhere: https://issues.dlang.org/show_bug.cgi?id=13489
Oct 29
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/29/17 3:10 PM, Nemanja Boric wrote:
 We've already reported this as a bug (I actually got quite burned on it, 
 trusting assert(float_value) to prevent NaN's escaping the function), 
 but there were different opinions on this, so it never got anywhere: 
 https://issues.dlang.org/show_bug.cgi?id=13489
Wow, interesting dialog there. I'm in the camp that nan should be evaluated as false. I don't think I like the idea that !nan is also false, because this makes !!nan true! TBH, I don't think I ever considered doing if(floatingpointvalue) to be a good idea in C or D. I generally stay away from float, and especially comparing to 0 directly. -Steve
Oct 30
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, October 30, 2017 11:04:32 Steven Schveighoffer via Digitalmars-d 
wrote:
 On 10/29/17 3:10 PM, Nemanja Boric wrote:
 We've already reported this as a bug (I actually got quite burned on it,
 trusting assert(float_value) to prevent NaN's escaping the function),
 but there were different opinions on this, so it never got anywhere:
 https://issues.dlang.org/show_bug.cgi?id=13489
Wow, interesting dialog there. I'm in the camp that nan should be evaluated as false. I don't think I like the idea that !nan is also false, because this makes !!nan true! TBH, I don't think I ever considered doing if(floatingpointvalue) to be a good idea in C or D. I generally stay away from float, and especially comparing to 0 directly.
Yeah. Honestly, I stay away from if(x) in general if x isn't a bool. I might occasionally do it for a pointer, but it's not like floating point types are the only ones that act badly with cast(bool) (e.g. dynamic arrays). It all works just fine if you understand what each of the types do with cast(bool), but it's just clearer if you make the check it explicit. I avoid floating point types on general principle though - not just in if statements. I'll use them when I need them, but if I can reasonably do something with an integral type instead, I will, because I don't want to deal with the inaccuracies of FP math. The fact that NaN == NaN is false and yet cast(bool)NaN is true though is just attrocious though. We aren't source compatible with C like C++ is, and yet we're still bound by it in so many small, stupid ways - largely because of the risk of ported code going badly. - Jonathan M Davis
Oct 30
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 30 October 2017 at 19:51:30 UTC, Jonathan M Davis 
wrote:
 Yeah. Honestly, I stay away from if(x) in general if x isn't a 
 bool.
That's the best option, even for ints. The proper way to cast to bool is to be explicit. A lot of the shorthand in C probably had to do with early CRTs not being able to display a lot of text. At this day and age readability (easy to scan quickly) should be more important than terseness.
 The fact that NaN == NaN is false and yet cast(bool)NaN is true 
 though is just attrocious though. We aren't source compatible 
 with C like C++ is, and yet we're still bound by it in so many 
 small, stupid ways - largely because of the risk of ported code 
 going badly.
I think some of the float semantics aren't in the standard, but in IEEE754. But many languages have picked up many of C's not-so-great design characteristics. I think largely because people who write their own compilers tend to program in C… so more cultural than rational.
Oct 30
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, October 30, 2017 21:47:50 Ola Fosheim Grøstad via Digitalmars-d 
wrote:
 On Monday, 30 October 2017 at 19:51:30 UTC, Jonathan M Davis
 The fact that NaN == NaN is false and yet cast(bool)NaN is true
 though is just attrocious though. We aren't source compatible
 with C like C++ is, and yet we're still bound by it in so many
 small, stupid ways - largely because of the risk of ported code
 going badly.
I think some of the float semantics aren't in the standard, but in IEEE754. But many languages have picked up many of C's not-so-great design characteristics. I think largely because people who write their own compilers tend to program in C… so more cultural than rational.
In D's case, Walter consciously tried to make sure that C code was either valid D code with the same semantics or that it wasn't valid D code so that you wouldn't get subtle errors when porting code from C to D. We aren't quite there (e.g. the semantics of a function parameter that's a static array aren't the same), but we're close. His reasoning makes a lot of sense, but it also means that we're still beholden to some of C's unsavory choices - albeit nowhere near as many as C++ is. So, in D's case at least, the decision to match C's semantics was very much rational. However, I'm sure that it's true that a number of the design decisions in D stem from the fact that both Walter and Andrei come from C/C++, and so they're going to tend to make design decisions similar to C/C++ by default. So, D ends up being different when there's a feature that's being purposefully designed to be different from C/C++, but it tends to be similar when there wasn't a strong reason to do something differently from C/C++. - Jonathan M Davis
Oct 30
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 30 October 2017 at 22:42:48 UTC, Jonathan M Davis 
wrote:
 However, I'm sure that it's true that a number of the design 
 decisions in D stem from the fact that both Walter and Andrei 
 come from C/C++, and so they're going to tend to make design 
 decisions similar to C/C++ by default. So, D ends up being 
 different when there's a feature that's being purposefully 
 designed to be different from C/C++, but it tends to be similar 
 when there wasn't a strong reason to do something differently 
 from C/C++.
But as we can see from this discussion of the elvis-operator those "rough edges" becomes viral over time as you add more features. So, whiley some rough edges might not be so visible when you have few features, they might be amplified as you add more features. C++ is a pretty good example of this. They add new stuff that tries to iron out some of the rough spots, and are fairly successful at it, but they have to do it in ways where they end up with features clashing or syntactical annoyances. I haven't used Rust much, but it is interesting that they managed to stay out of the C-mold. Most other main-stream compiled languages has not. Anyway, it often takes several years of practice to discover that neat features aren't so good after all. I probably wrote much more terse C code in the beginning. I also have changed my ways with C++, too much sigils so now I am using "and" and "or" for booleans. Too much reuse of "&&" and "&" makes code less readable. As languages get more complex feature-wise I think it becomes ever more important to streamline. Which is kinda the opposite of what happens when you let the design over decades. (so you have to put more work into streamlining how you write code).
Oct 30
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 30 October 2017 at 23:09:43 UTC, Ola Fosheim Grøstad 
wrote:
 Which is kinda the opposite of what happens when you let the 
 design over decades. (so you have to put more work into 
 streamlining how you write code).
I meant to say that this is the opposite of what happens when you let the language design evolve over years, so at the end the programmers have to compensate by focusing more on streamlining the aesthetics of their code…
Oct 30
prev sibling next sibling parent crimaniak <crimaniak gmail.com> writes:
On Monday, 30 October 2017 at 15:04:32 UTC, Steven Schveighoffer 
wrote:
 https://issues.dlang.org/show_bug.cgi?id=13489
...
 I'm in the camp that nan should be evaluated as false. I don't 
 think I like the idea that !nan is also false, because this 
 makes !!nan true!
The problem of converting NaN to binary is that NaN introduces a ternary logic. There is no way to convert the ternary value to binary without loss. If we want to be consistent we have to convert it to Nullable!bool, but not to bool. So, to introduce Elvis operator for floats in language and to stay consistent it needs also Nullable/Not-Nullable as part of language type system, IMHO.
Oct 30
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Oct 30, 2017 at 01:51:30PM -0600, Jonathan M Davis via Digitalmars-d
wrote:
 On Monday, October 30, 2017 11:04:32 Steven Schveighoffer via Digitalmars-d 
 wrote:
[...]
 TBH, I don't think I ever considered doing if(floatingpointvalue) to
 be a good idea in C or D. I generally stay away from float, and
 especially comparing to 0 directly.
Yeah. Honestly, I stay away from if(x) in general if x isn't a bool. I might occasionally do it for a pointer, but it's not like floating point types are the only ones that act badly with cast(bool) (e.g. dynamic arrays). It all works just fine if you understand what each of the types do with cast(bool), but it's just clearer if you make the check it explicit.
+1 for writing it explicitly. It reminds me of C code along these lines: if (some_function(x)) { cleanup(); return ERROR; } if (some_other_function(y)) { return SUCCESS; } Typical result of the widespread C convention of returning int that can mean ... basically anything. Some functions return 0 to mean error, some return 0 to mean success, and of course, on the caller's side you assume clairvoyance on the part of the would-be reader of your code to know what was intended. Which leads to fun (or insanity) like this: int f(char *s, int x) { return strcmp(s, "verbose") && !printf("The result is ") || x && !printf("success ") || isalnum(x) && !printf("unknown ") || printf("(%d)", x) && printf("\n"); } Back in D land, I was so happy way back when, when dmd began rejecting this code: S* sp; while ((sp = getS()) && someCondition) { ... } and I had to write this instead: S* sp; while ((sp = getS()) !is null && someCondition) { ... } Added a whole new level of readability to my code. Nowadays, I don't even like using implicit conversions to bool anymore. Intent is much better conveyed with an explicit comparison to 0 or 1 or whatever. Saving a few keystrokes simply isn't worth the amount of time and mental fatigue incurred when you need to debug something that's caused by wrong interpretation of return values. T -- People tell me I'm stubborn, but I refuse to accept it!
Oct 30
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 29 October 2017 at 18:02:25 UTC, Jonathan M Davis 
wrote:
 Sounds like a bug to me. NaN is supposed to be false whenever 
 it's used in a comparison. If it it's true when cast directly 
 to bool, then that's inconsistent.
It is consistent with C...
Oct 29
prev sibling next sibling parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu 
wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym operator to D.

 Razvan Nitu has already done a good part of the work:

 https://github.com/dlang/dmd/pull/7242
 https://github.com/dlang/dlang.org/pull/1917
 https://github.com/dlang/dlang.org/pull/1918
Is it going to be something similar (or the same) as in Kotlin? (Reference: https://kotlinlang.org/docs/reference/null-safety.html#elvis-operator ) I see from comments that different people think of it in a different way. I suggest them to read this section from Kotlin docs to understand the reasoning behind the elvis operator.
Oct 30
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/30/17 7:32 AM, Dejan Lekic wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the homonym 
 operator to D.

 Razvan Nitu has already done a good part of the work:

 https://github.com/dlang/dmd/pull/7242
 https://github.com/dlang/dlang.org/pull/1917
 https://github.com/dlang/dlang.org/pull/1918
Is it going to be something similar (or the same) as in Kotlin? (Reference: https://kotlinlang.org/docs/reference/null-safety.html#elvis-operator ) I see from comments that different people think of it in a different way. I suggest them to read this section from Kotlin docs to understand the reasoning behind the elvis operator.
From looking quickly at that, but not having any experience with Kotlin (but having some experience with Swift), I think this really requires a concept of Nullable types being a builtin feature. The idea is to have something that always results in a non-null value. Swift uses the operator ?? to do the same thing (search for ?? on this page: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html) I think D has the capability of doing the same thing, even with Nullable being a library type, but would have to be pervasive in usage to look like Kotlin. At least the operator itself fits into the usage of Null or not types. Of course, the D version is much more adaptable, as any concept of "Null" or "Invalid" can be ascribed to a type via the opCast(bool) function. -Steve
Oct 30
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/30/17 7:32 AM, Dejan Lekic wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the homonym 
 operator to D.

 Razvan Nitu has already done a good part of the work:

 https://github.com/dlang/dmd/pull/7242
 https://github.com/dlang/dlang.org/pull/1917
 https://github.com/dlang/dlang.org/pull/1918
Is it going to be something similar (or the same) as in Kotlin? (Reference: https://kotlinlang.org/docs/reference/null-safety.html#elvis-operator ) I see from comments that different people think of it in a different way. I suggest them to read this section from Kotlin docs to understand the reasoning behind the elvis operator.
The principle of least astonishment indicates we should do what the lowering does: expr1 ?: expr2 ==> (x => x ? x : expr2)(expr1) An approach that does things any differently would have a much more difficult time. It is understood (and expected) that other languages have subtly different takes on the operator. Andrei
Oct 30
parent MrSmith <mrsmith33 yandex.ru> writes:
On Monday, 30 October 2017 at 19:46:54 UTC, Andrei Alexandrescu 
wrote:
 I see from comments that different people think of it in a 
 different way. I suggest them to read this section from Kotlin 
 docs to understand the reasoning behind the elvis operator.
The principle of least astonishment indicates we should do what the lowering does: expr1 ?: expr2 ==> (x => x ? x : expr2)(expr1) An approach that does things any differently would have a much more difficult time. It is understood (and expected) that other languages have subtly different takes on the operator. Andrei
I may add that the same logic is used in .get(key, defaultValue) method of Associative Arrays
Nov 05
prev sibling next sibling parent reply Mark <smarksc gmail.com> writes:
On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu 
wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym operator to D.

 [...]

 Thanks,

 Andrei
The Elvis operator's purpose is to make working with null easier, but isn't null "The Billion Dollar Mistake"? :
Oct 31
parent reply bauss <jj_1337 live.dk> writes:
On Tuesday, 31 October 2017 at 08:15:24 UTC, Mark wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei 
 Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym operator to D.

 [...]

 Thanks,

 Andrei
The Elvis operator's purpose is to make working with null easier, but isn't null "The Billion Dollar Mistake"? :
Null is not the problem. The usage of types that can be null is the problem. Although only concepts, these will solve most issues with null references. https://github.com/visionlang/vsl/wiki/%2316-Null-safety https://github.com/visionlang/vsl/wiki/%2325-not-null-types https://github.com/visionlang/vsl/wiki/%2336-Error-Handling#nothrow
Oct 31
parent Mark <smarksc gmail.com> writes:
On Tuesday, 31 October 2017 at 19:39:17 UTC, bauss wrote:
 On Tuesday, 31 October 2017 at 08:15:24 UTC, Mark wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei 
 Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym operator to D.

 [...]

 Thanks,

 Andrei
The Elvis operator's purpose is to make working with null easier, but isn't null "The Billion Dollar Mistake"? :
Null is not the problem. The usage of types that can be null is the problem. Although only concepts, these will solve most issues with null references. https://github.com/visionlang/vsl/wiki/%2316-Null-safety https://github.com/visionlang/vsl/wiki/%2325-not-null-types https://github.com/visionlang/vsl/wiki/%2336-Error-Handling#nothrow
I don't know... Personally I prefer Haskell's approach with Option types, but maybe it's too late to add something like that to D.
Nov 01
prev sibling next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu 
wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym operator to D.
I'd like to mention null-coalescing assignment syntax. Perl has `$a //= $b`, and PHP has voted to support `$a ??= $b`, expanding to `$a = $a ?? $b`. Rationale: // The following lines are doing the same $this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value'; // Instead of repeating variables with long names, the equal coalesce operator is used $this->request->data['comments']['user_id'] ??= 'value'; https://wiki.php.net/rfc/null_coalesce_equal_operator I expect D could do the same with `a ?:= b` or use the `??` operator syntax. Just from memory, I think I would use null coalescing assignments more than null coalescing comparisons.
Nov 02
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Nov 02, 2017 at 12:50:47PM +0000, Nick Treleaven via Digitalmars-d
wrote:
[..]
 I'd like to mention null-coalescing assignment syntax. Perl has `$a
 //= $b`, and PHP has voted to support `$a ??= $b`, expanding to `$a =
 $a ?? $b`.
[...]
 I expect D could do the same with `a ?:= b` or use the `??` operator syntax.
Isn't the `??` syntax what the Elvis operator `?:` supposed to do? Given that the proposed Elvis operator would be `?:`, I'd expect the corresponding assignment operator would be `?:=`, in following the pattern of the other op-assign operators.
 Just from memory, I think I would use null coalescing assignments more
 than null coalescing comparisons.
If we're going to have one of them, might as well have both. T -- I think the conspiracy theorists are out to get us...
Nov 02
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, November 02, 2017 09:46:06 H. S. Teoh via Digitalmars-d wrote:
 On Thu, Nov 02, 2017 at 12:50:47PM +0000, Nick Treleaven via Digitalmars-d
 wrote: [..]

 I'd like to mention null-coalescing assignment syntax. Perl has `$a
 //= $b`, and PHP has voted to support `$a ??= $b`, expanding to `$a =
 $a ?? $b`.
[...]
 I expect D could do the same with `a ?:= b` or use the `??` operator
 syntax.
Isn't the `??` syntax what the Elvis operator `?:` supposed to do? Given that the proposed Elvis operator would be `?:`, I'd expect the corresponding assignment operator would be `?:=`, in following the pattern of the other op-assign operators.
In a previous thread, it was stated that in other languages (no idea which ones), ?? tests specifically for null, whereas ?: tests for true. Other, related operators were discussed as well - including ?. - which if I understood correctly, allows you to call a member function only if the object isn't null. But going into other operators like that starts taking this well beyond the basic idea of shortening the ternary operator for the x ? x : y case like the elvis operator does, and for better or worse, that takes the DIP in a whole new direction. However, if we do add ?:, I think that it's pretty clear that some folks will be asking for stuff like ?. or ?:=, since I think that it's largely the case that the folks who want the elvis operator are interested in the related operators. However, their usefulness seems to be mostly predicated on the idea that you're doing a lot with class references or pointers which might be null and where you would want to do an operation with them if they're non-null and just skip that code if they're null. And given how little idiomatic D code uses classes, I don't know how useful they'll be in practice in D. It's likely to depend a lot on your coding style. I'm quite sure that I would find them borderline useless, but some of the folks who are highly interested in them may find a lot of use for them. I don't know. - Jonathan M Davis
Nov 02
prev sibling next sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu 
wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym operator to D.
It's easy to write in function form: auto orElse(T)(T a, lazy T b) { return a ? a : b; } writeln(args[1].length.orElse(fibonacci(50))); This version can also be specialized for things like Nullable, where you can't necessarily cast it safely to a boolean but have a check for validity. Is it that valuable to have an operator for it instead? As an aside, I believe feepingcreature had a custom infix operator for this sort of thing defined, so you could write: (a /or/ b /or/ c).doStuff(); The implementation (along with /and/) is left as an exercise to the reader.
Nov 05
next sibling parent reply bauss <jj_1337 live.dk> writes:
On Monday, 6 November 2017 at 00:20:09 UTC, Neia Neutuladh wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei 
 Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym operator to D.
It's easy to write in function form: auto orElse(T)(T a, lazy T b) { return a ? a : b; } writeln(args[1].length.orElse(fibonacci(50))); This version can also be specialized for things like Nullable, where you can't necessarily cast it safely to a boolean but have a check for validity. Is it that valuable to have an operator for it instead? As an aside, I believe feepingcreature had a custom infix operator for this sort of thing defined, so you could write: (a /or/ b /or/ c).doStuff(); The implementation (along with /and/) is left as an exercise to the reader.
Sure you might be able to write it easily, but pretty much everyone writes a function like that in their projects and you don\t really know the implementation always and you have to remember the exact name or else you end up writing the function yourself in your project to make sure the implementation is exactly like that, which in fact turns out to be re-inventing the wheel. By having an official syntax for it, you don't end up with code duplication like that and the implementation details of the behavior is clear. Because in some implementation "orElse()" might only check if the value is null and not necessary if the value is true. Ex. one might implement it like: auto orElse(T)(T a, lazy T b) { return a !is null ? a : b; } Such implementation detail is not clear from the call-side. However with an official implementation you know exactly how it will behave and nothing is obscure like this.
Nov 05
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, November 06, 2017 07:10:43 bauss via Digitalmars-d wrote:
 On Monday, 6 November 2017 at 00:20:09 UTC, Neia Neutuladh wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei

 Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the
 homonym operator to D.
It's easy to write in function form: auto orElse(T)(T a, lazy T b) { return a ? a : b; } writeln(args[1].length.orElse(fibonacci(50))); This version can also be specialized for things like Nullable, where you can't necessarily cast it safely to a boolean but have a check for validity. Is it that valuable to have an operator for it instead? As an aside, I believe feepingcreature had a custom infix operator for this sort of thing defined, so you could write: (a /or/ b /or/ c).doStuff(); The implementation (along with /and/) is left as an exercise to the reader.
Sure you might be able to write it easily, but pretty much everyone writes a function like that in their projects and you don\t really know the implementation always and you have to remember the exact name or else you end up writing the function yourself in your project to make sure the implementation is exactly like that, which in fact turns out to be re-inventing the wheel. By having an official syntax for it, you don't end up with code duplication like that and the implementation details of the behavior is clear. Because in some implementation "orElse()" might only check if the value is null and not necessary if the value is true. Ex. one might implement it like: auto orElse(T)(T a, lazy T b) { return a !is null ? a : b; } Such implementation detail is not clear from the call-side. However with an official implementation you know exactly how it will behave and nothing is obscure like this.
That might be an argument for having an official implementation, but it's not really an argument for why it should be built into the language; it could just as easily be in Phobos if that's all that matters. - Jonathan M Davis
Nov 06
parent reply Satoshi <satoshi rikarin.org> writes:
On Monday, 6 November 2017 at 08:06:54 UTC, Jonathan M Davis 
wrote:
 On Monday, November 06, 2017 07:10:43 bauss via Digitalmars-d 
 wrote:
 On Monday, 6 November 2017 at 00:20:09 UTC, Neia Neutuladh 
 wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei

 Alexandrescu wrote:
 [...]
It's easy to write in function form: auto orElse(T)(T a, lazy T b) { return a ? a : b; } writeln(args[1].length.orElse(fibonacci(50))); This version can also be specialized for things like Nullable, where you can't necessarily cast it safely to a boolean but have a check for validity. Is it that valuable to have an operator for it instead? As an aside, I believe feepingcreature had a custom infix operator for this sort of thing defined, so you could write: (a /or/ b /or/ c).doStuff(); The implementation (along with /and/) is left as an exercise to the reader.
Sure you might be able to write it easily, but pretty much everyone writes a function like that in their projects and you don\t really know the implementation always and you have to remember the exact name or else you end up writing the function yourself in your project to make sure the implementation is exactly like that, which in fact turns out to be re-inventing the wheel. By having an official syntax for it, you don't end up with code duplication like that and the implementation details of the behavior is clear. Because in some implementation "orElse()" might only check if the value is null and not necessary if the value is true. Ex. one might implement it like: auto orElse(T)(T a, lazy T b) { return a !is null ? a : b; } Such implementation detail is not clear from the call-side. However with an official implementation you know exactly how it will behave and nothing is obscure like this.
That might be an argument for having an official implementation, but it's not really an argument for why it should be built into the language; it could just as easily be in Phobos if that's all that matters. - Jonathan M Davis
You need additional import for this verbose syntax https://en.wikipedia.org/wiki/Syntactic_sugar Why we have operators overloading when we could have Equals() and stuff like in java? Why we have arr ~= arr2 when we could have Array.mergeArrays(arr, arr2) instead? Look, this operator does not break anything. If you don't want to use it, just don't, but why do you force everyone else to not to use it, just because it is not adding anything "more valuable" than just better syntax?
Nov 06
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, November 06, 2017 09:26:24 Satoshi via Digitalmars-d wrote:
 Look, this operator does not break anything. If you don't want to
 use it, just don't, but why do you force everyone else to not to
 use it, just because it is not adding anything "more valuable"
 than just better syntax?
_Everything_ that is added to the language complicates it further. It's one more thing that everyone learning the language has to learn and know and potentially deal with in code. Obviously, some things are worth the extra complication, or we'd all be programming in C, but there is always a cost to adding something, and the feature needs to be worth that cost. Granted, the elvis operator as proposed is not all that complicated, but adding it does make the language that much more complex, and it really doesn't do much. All it does is take the expression x ? x : y and make it x ?: y It saves 2 characters plus the length of the variable name. That's it. And it's optimizing an idiom that isn't going to tend to show up much in idiomatic D code. It seems to be targeted primarily at code that does a lot with classes and is written in such a way that it's not clear whether a class reference should be null or not, whereas most D code doesn't do much with classes. Rather, it does a lot with ranges and structs on the stack. Obviously, some code uses classes, and I expect that some code would benefit from the elvis operator, but I dispute that it's a common enough idiom to merit being added the language. Personally, while I frequently use the ternary operator, I almost never end up with the left and middle branches being the same, which is the only place that the elvis operator would be useful. And I don't think that Phobos does it much either. So, unless a lot of D code out there is using a drastically different coding style that does a lot with null, the elvis operator will not help much D code. So, IMHO, the elvis operator adds very little value, and I'd just as soon not see the language complicated further without adding real value in the process. So, I'll argue against it, but ultimately, it's not my decision. Rather, it's up to Walter and Andrei, and they'll decide what they decide. But don't expect anyone not to be unhappy about a feature being added to the language when they don't think that the feature adds value, because there is always a cost to a new feature. The difference is that you think that the feature is worth the cost, not that the folks who don't want to use the feature don't have to pay the cost. - Jonathan M Davis
Nov 06
next sibling parent Biotronic <simen.kjaras gmail.com> writes:
On Monday, 6 November 2017 at 10:12:11 UTC, Jonathan M Davis 
wrote:
 x ? x : y

 and make it

 x ?: y

 It saves 2 characters plus the length of the variable name. 
 That's it.
I find I often use this in C# with a more complex expression on the left-hand side, like a function call. A quick search shows more than 2/3 of my uses are function calls or otherwise significantly more complex than a variable. Also, it works great in conjunction with the null conditional: foo.Select(a => bar(a, qux)).FirstOrDefault?.Name ?? "not found";
 It seems to be targeted primarily at code that does a lot with 
 classes and is written in such a way that it's not clear 
 whether a class reference should be null or not, whereas most D 
 code doesn't do much with classes.
In my C# code, it's used with strings and Nullable<T> more often than with classes. Given my own experience with the ?? operator, I'd argue it's probably not worth it without also including null conditional (?.). A quick search in a few projects indicate roughly half the uses of ?? also use ?.. -- Biotronic
Nov 06
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 6 November 2017 at 10:12:11 UTC, Jonathan M Davis 
wrote:
 All it does is take the expression

 x ? x : y

 and make it

 x ?: y
Yes, that is an issue because it means that typos no longer are caught. E.g. if you accidentally comment out or delete the second expression. Which is why I think ?? or some other choice would offer better usability.
 The difference is that you think that the feature is worth the 
 cost, not that the folks who don't want to use the feature 
 don't have to pay the cost.
Indeed.
Nov 06
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 11/05/2017 07:20 PM, Neia Neutuladh wrote:
 On Saturday, 28 October 2017 at 11:38:52 UTC, Andrei Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the homonym 
 operator to D.
It's easy to write in function form:   auto orElse(T)(T a, lazy T b)   {     return a ? a : b;   }   writeln(args[1].length.orElse(fibonacci(50))); This version can also be specialized for things like Nullable, where you can't necessarily cast it safely to a boolean but have a check for validity. Is it that valuable to have an operator for it instead? As an aside, I believe feepingcreature had a custom infix operator for this sort of thing defined, so you could write:   (a /or/ b /or/ c).doStuff(); The implementation (along with /and/) is left as an exercise to the reader.
If a DIP emerges, it would need to present such alternatives and argue how it adds value over them. -- Andrei
Nov 06
prev sibling parent reply Adam Wilson <flyboynw gmail.com> writes:
On 10/28/17 04:38, Andrei Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the homonym
 operator to D.

 Razvan Nitu has already done a good part of the work:

 https://github.com/dlang/dmd/pull/7242
 https://github.com/dlang/dlang.org/pull/1917
 https://github.com/dlang/dlang.org/pull/1918

 What's needed is a precise DIP that motivates the feature properly and
 provides a good proposal for it. I'm no fan of bureaucracy but we really
 need to be pedantic about introducing language features. Walter argued
 thusly in a PR, and I agree:

 "I'm concerned that the elvis operator is not well understood, and we
 shouldn't be designing it in the comments section here. A DIP needs to
 be written. Things like operator precedence, side effects, type
 resolution, comparison with the operator in other languages, grammar
 changes, lvalues, how it would appear in the generated .di file if it
 isn't its own operator, etc., should be addressed."

 A lowering looks like the straightforward approach, of the kind:

 expr1 ?: expr2

 ==>

 (x => x ? x : expr2)(expr1)

 Who wants to join Razvan in Project Elvis?


 Thanks,

 Andrei
C# has extensive experience with this operator and I think it would be wise to study the history of what they did and why the did it. NOTE: I understand that other languages have it, and there are variations on the theme, but C# has many similarities to D and extensive "in practice" idioms. C# got the Elvis operator before it got the Null Conditional operator. In C# it only covers the case: a == null. The reason is that in practice most devs only use it like so: a != null ? a : b. The funny thing is that it was almost never used. Some years later the C# team introduces the Null-Conditional operator: ?. which allows you to write: obj1?.obj2?.prop3 ?? constant. NOW people start using Null Coalescing all over the place. I am all for the Elvis operator, however I have two reservations about it. The first is that I don't see much use for it without a null-conditional. The second is that the current proposed syntax ?: is MUCH to easily confused with ?. This is not easy to read: obj1?.obj2?.prop3?:constant. When designing syntax sugar, ergonomics are very important, otherwise people won't use it. Microsoft spent a LOT of time and treasure to learn these lessons for us. I see no reason to ignore them just because "we don't like Microsoft" My proposal would be to copy what MSFT did, expect that I would I would introduce both operators at the same time. Syntax as follows: obj1?.obj2?.prop3 ?? constant In practice I don't see much use of the idiom outside of null's. The ONLY other thing that would work there is a boolean field and you might as well just return the boolean itself because the return values have to match types. For example: return obj1.bool ?? obj2 //Error: incorrect return type return obj1 ?? obj2 // Pass: if same type I cannot actually imagine a scenario outside of objects (including strings) where you could actually use it since the left-hand side MUST evaluate to a boolean. Also, I am going to start repeating this mantra: Just because something CAN be done in the library, does not mean it SHOULD be done in the library. Ergonomics matters. Yes, I understand that D is a powerful language, but Syntax Sugar has it's place in taking common idioms and standardizing them in the language itself (English is loaded with stuff like that) so that everyone can "speak the same language". -- Adam Wilson IRC: LightBender import quiet.dlang.dev;
Nov 06
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Monday, 6 November 2017 at 19:13:59 UTC, Adam Wilson wrote:
 On 10/28/17 04:38, Andrei Alexandrescu wrote:
 Walter and I decided to kick-off project Elvis for adding the 
 homonym
 operator to D.

 Razvan Nitu has already done a good part of the work:

 https://github.com/dlang/dmd/pull/7242
 https://github.com/dlang/dlang.org/pull/1917
 https://github.com/dlang/dlang.org/pull/1918
Some years later the C# team introduces the Null-Conditional operator: ?. which allows you to write: obj1?.obj2?.prop3 ?? constant. NOW people start using Null Coalescing all over the place.
So C# embraced the Null. Everything is nullable and you are expected to ?. proof it if you don’t know in advance. In contrast other languages (e.g. Scala) decided to discourage the use of null in favor of algebraic type Option!T and even remove the null all together.
 I am all for the Elvis operator, however I have two 
 reservations about it.
To me the biggest reservation is “embracing the null” that this entails. In other words since we dedicate a feature to support nulls that indicates null return is an endorsed aproach to e.g. model APIs and libraries that deal with optional values. I’d argue this NOT what we want. Nullability is best captured in the typesystem even if in the form of Nullable!T.
Nov 06
parent reply Jacob Carlborg <doob me.com> writes:
On 2017-11-06 20:40, Dmitry Olshansky wrote:

 I’d argue this NOT what we want. Nullability is best captured in the 
 typesystem even if in the form of Nullable!T.
Yeah, it would be better if the elvis operator good integrate with a nullable/option type as well in addition to null. -- /Jacob Carlborg
Nov 06
parent Meta <jared771 gmail.com> writes:
On Monday, 6 November 2017 at 19:55:13 UTC, Jacob Carlborg wrote:
 On 2017-11-06 20:40, Dmitry Olshansky wrote:

 I’d argue this NOT what we want. Nullability is best captured 
 in the typesystem even if in the form of Nullable!T.
Yeah, it would be better if the elvis operator good integrate with a nullable/option type as well in addition to null.
What's the point when we can already do it easily in a library, and arguably with better ergonomics? (http://forum.dlang.org/post/fshlmahxfaeqtwjbjouz forum.dlang.org) auto tree = new Node(1, new Node(2), new Node(3, null, new Node(4) ) ); import std.stdio; writeln(safeDeref(tree).right.right.val.orElse(-1)); writeln(safeDeref(tree).left.right.left.right.orElse(null)); writeln(safeDeref(tree).left.right.left.right.val.orElse(-1)); vs. writeln(tree?. right?.right?.val ?: -1); writeln(tree?.left?.right?.left?.right); writeln(tree?.left?.right?.left?.right?.val ?: -1); The functionality is probably a good idea, but a library solution is doable today without any acrobatics.
Nov 06
prev sibling parent Michael <michael toohuman.io> writes:
I can't quite see why this proposal is such a big deal to people 
- as has been restated, it's just a quick change in the parser 
for a slight contraction in the code, and nothing 
language-breaking, it's not a big change to the language at all.

On Monday, 6 November 2017 at 19:13:59 UTC, Adam Wilson wrote:
 I am all for the Elvis operator, however I have two 
 reservations about it. The first is that I don't see much use 
 for it without a null-conditional. The second is that the 
 current proposed syntax ?: is MUCH to easily confused with ?.

 This is not easy to read: obj1?.obj2?.prop3?:constant.

 When designing syntax sugar, ergonomics are very important, 
 otherwise people won't use it. Microsoft spent a LOT of time 
 and treasure to learn these lessons for us. I see no reason to 
 ignore them just because "we don't like Microsoft"

 My proposal would be to copy what MSFT did, expect that I would 
 I would introduce both operators at the same time.

 Syntax as follows: obj1?.obj2?.prop3 ?? constant

 In practice I don't see much use of the idiom outside of 
 null's. The ONLY other thing that would work there is a boolean 
 field and you might as well just return the boolean itself 
 because the return values have to match types.
I feel this is kind of embellished somewhat. When you write
 This is not easy to read: obj1?.obj2?.prop3?:constant.
you're not separating it out as you do when you write your preferred version:
 Syntax as follows: obj1?.obj2?.prop3 ?? constant
How is
 obj1?.obj2?.prop3 ?: constant
not as easy to read as
 obj1?.obj2?.prop3 ?? constant
to me they are the same in terms of readability, only with ?? you have greater chances of mistyping and adding a second ? in there somewhere, whereas the ?: is just a contraction of the current syntax, I really don't think it's that difficult, so I'm not sure what people's hang-ups are, but I don't think the argument that ?? is easier to read than ?: holds any weight here, because one *is* a change to the language, and the other is a change to the parser and a contraction of a standard convention.
Nov 06