October 29, 2017
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.

October 29, 2017
On Sunday, October 29, 2017 16:18:43 Ola Fosheim Grøstad 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


October 29, 2017
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.

October 29, 2017
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.

> 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


October 29, 2017
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.
October 29, 2017
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


October 29, 2017
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
October 29, 2017
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
October 29, 2017
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...

October 29, 2017
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*