Jump to page: 1 2
Thread overview
This syntax regarding null checking baffles me
Jan 07
Basile B.
Jan 07
Dukc
Jan 09
kdevel
Jan 10
kdevel
Jan 10
kdevel
Jan 10
kdevel
Jan 10
ag0aep6g
January 07
if (c !is null) Why?????

Would it be simpler to type

if (c is not null)

on a related note. Why are not we allowed to do this?

if (c != null)

when other languages such as c#/java allow for it?

-Alex
January 07
On Thursday, 7 January 2021 at 04:57:55 UTC, 12345swordy wrote:
> if (c !is null) Why?????
>
> Would it be simpler to type
>
> if (c is not null)
>
> on a related note. Why are not we allowed to do this?
>
> if (c != null)

it is allowed

>
> when other languages such as c#/java allow for it?
>
> -Alex

"is" checks the address whereas "==" and "!=" eventually takes the path of operator overloading, i.e opEquals.

January 07
On Thursday, 7 January 2021 at 05:14:44 UTC, Basile B. wrote:
> On Thursday, 7 January 2021 at 04:57:55 UTC, 12345swordy wrote:
>> if (c !is null) Why?????
>>
>> Would it be simpler to type
>>
>> if (c is not null)
>>
>> on a related note. Why are not we allowed to do this?
>>
>> if (c != null)
>
> it is allowed
>
"For class objects, the == and != operators are intended to compare the contents of the objects, however an appropriate opEquals override must be defined for this to work. The default opEquals provided by the root Object class is equivalent to the is operator. Comparing against null is invalid, as null has no contents. Use the is and !is operators instead."

Not allowed for classes apparently.

-Alex
January 07
On Thursday, 7 January 2021 at 04:57:55 UTC, 12345swordy wrote:
> if (c !is null) Why?????
>
> Would it be simpler to type
>
> if (c is not null)

Saves 3 character per use, I guess. And keeps one operator in one keyword. Matter of taste, and whoever designed this obviously had to pick something.


January 07
On Thursday, 7 January 2021 at 15:37:44 UTC, Dukc wrote:
> On Thursday, 7 January 2021 at 04:57:55 UTC, 12345swordy wrote:
>> if (c !is null) Why?????
>>
>> Would it be simpler to type
>>
>> if (c is not null)
>
> Saves 3 character per use, I guess. And keeps one operator in one keyword. Matter of taste, and whoever designed this obviously had to pick something.

I don't think not is reserved anywhere else so it would mean adding a new terminal to the grammar too, and d already has a lot.
January 07
On 1/7/21 9:13 AM, 12345swordy wrote:
> On Thursday, 7 January 2021 at 05:14:44 UTC, Basile B. wrote:
>> On Thursday, 7 January 2021 at 04:57:55 UTC, 12345swordy wrote:
>>> if (c !is null) Why?????
>>>
>>> Would it be simpler to type
>>>
>>> if (c is not null)
>>>
>>> on a related note. Why are not we allowed to do this?
>>>
>>> if (c != null)
>>
>> it is allowed
>>
> "For class objects, the == and != operators are intended to compare the contents of the objects, however an appropriate opEquals override must be defined for this to work. The default opEquals provided by the root Object class is equivalent to the is operator. Comparing against null is invalid, as null has no contents. Use the is and !is operators instead."
> 
> Not allowed for classes apparently.

Historically (before this restriction was added), when you compared 2 class objects, the code:

obj != expr

translated directly to:

!obj.opEquals(expr)

Which, if obj was null, resulted in a segmentation fault.

Thus the case of:

obj != null

is both error prone and ironic:

if(obj != null) { /* use obj */ }

This crashes if obj actually is null. Therefore, the syntax `obj !is null` is required.

You could find somewhere on these forums where I advocated for this, and Walter finally agreed.

Since then, the code for equality now translates to:

object.opEquals(obj1, obj2)

which will not crash, even if the object that isn't null improperly handles a comparison with null.

However, it's still more "correct" to say `is null` or `!is null`, as technically there's no other comparison that makes sense. So we *could* relax the restriction, but I think the existing expression is clearer.

-Steve
January 07
On Thursday, January 7, 2021 8:55:52 AM MST Max Haughton via Digitalmars-d wrote:
> On Thursday, 7 January 2021 at 15:37:44 UTC, Dukc wrote:
> > On Thursday, 7 January 2021 at 04:57:55 UTC, 12345swordy wrote:
> >> if (c !is null) Why?????
> >>
> >> Would it be simpler to type
> >>
> >> if (c is not null)
> >
> > Saves 3 character per use, I guess. And keeps one operator in one keyword. Matter of taste, and whoever designed this obviously had to pick something.
>
> I don't think not is reserved anywhere else so it would mean adding a new terminal to the grammar too, and d already has a lot.

That and using ! is more consistent with what the C family of languages typically does. C-derived languages don't typically try to make sentences like "is not" would do. Having is and !is is also more consistent with == and !=. "is not" wouldn't really fit the rest of the language.

Ultimately, how you feel about it probably comes down primarily to taste and what you're used to, but I'm sure that Walter would have wanted to avoid adding an extra keyword just for this. So, I would have expected him to reject "is not" on that basis, but my guess is that he never even considered it. Once he'd decided on is, !is was probably simply the obvious choice given the other operators that D has and Walter's C/C++ background.

- Jonathan M Davis



January 09
On Thursday, 7 January 2021 at 22:42:40 UTC, Jonathan M Davis wrote:

[...]

> That and using ! is more consistent with what the C family of languages typically does. C-derived languages don't typically try to make sentences like "is not" would do. Having is and !is is also more consistent with == and !=. "is not" wouldn't really fit the rest of the language.

Idiomatic C is of course [1]

   char *p = ...
   if (p) ...

This form seems to be applicable for pointers in D, too. And also
for class variables

   class C { ...
   C c;

I wonder if there is a difference between

   if (c)

and

   if (c ! is null).

[1] https://stackoverflow.com/questions/3825668/checking-for-null-pointer-in-c-c/3825704#3825704
January 10
On Saturday, January 9, 2021 2:02:33 PM MST kdevel via Digitalmars-d wrote:
> On Thursday, 7 January 2021 at 22:42:40 UTC, Jonathan M Davis wrote:
>
> [...]
>
> > That and using ! is more consistent with what the C family of languages typically does. C-derived languages don't typically try to make sentences like "is not" would do. Having is and !is is also more consistent with == and !=. "is not" wouldn't really fit the rest of the language.
>
> Idiomatic C is of course [1]
>
>     char *p = ...
>     if (p) ...
>
> This form seems to be applicable for pointers in D, too. And also for class variables
>
>     class C { ...
>     C c;
>
> I wonder if there is a difference between
>
>     if (c)
>
> and
>
>     if (c ! is null).
>
> [1] https://stackoverflow.com/questions/3825668/checking-for-null-pointer-in-c-c /3825704#3825704

IIRC, if the class overrides opCast for bool, then

    if(c)

will cast the object to bool and use the result for the if condition, whereas

    if(c !is null)

always checks whether the reference is null.

- Jonathan M Davis



January 10
On Sunday, 10 January 2021 at 10:42:48 UTC, Jonathan M Davis wrote:

[...]

> IIRC, if the class overrides opCast for bool, then
>
>     if(c)
>
> will cast the object to bool and use the result for the if condition,

Well, no:

```null.d
import std.stdio: writeln;
class C {
   bool opCast ()
   {
      return true;
   }
}

void bar (C c)
{
   if (c) writeln (3);
   else writeln (2);
}

void foo (ref C c)
{
   if (c) writeln (5);
   else writeln (4);
}

void main ()
{
   C c; // null ref
   if (c) writeln (1);
   else writeln (0);
   bar (c);
   foo (c);
   auto x = cast (bool) c; // crash as expected
}
```

$ ./null
0
2
4
Segmentation fault

BTW: Correctly using variables of class or in case below of AA type
takes some getting used to. A few day ago I ran into a bug with a helper
function "merge" in a context like this:

```ini.d
import std.stdio;
void merge(T) (T a, T b)
{
   foreach (k, v; b) {
      if (k in a)
         throw new Exception ("key <" ~ k ~ "> already set");
      a[k] = v;
   }
}

void main ()
{
   string[string] data;
   data = ["a": "A"]; // <---- comment me out! (*)
   auto other_data = ["x": "X"];
   data.merge (other_data);
   writeln (data);
}
```

As expected one gets:

   $ dmd ini.d && ./ini
   ["a":"A", "x":"X"]

Now it happened that in the course of development there was a code
path in which data was not initialized (*). Guess what the output was!

   $ dmd ini.d && ./ini
   []

A void merge(T) (ref T a, T b) is required to cover the uninitialzed
case. That was a rather surprising experience.
« First   ‹ Prev
1 2