March 25, 2013
On Mon, 25 Mar 2013 11:44:22 -0400, Jacob Carlborg <doob@me.com> wrote:

> I just started to use this in one place :(
>
> if (auto a = getArray()) { }
>
> getArray returns null if now array exists.

That one is not as easy.  It will have to be split into two lines, the declaration and the test.

-Steve
March 25, 2013
25-Mar-2013 21:09, Phil Lavoie пишет:
> On Monday, 25 March 2013 at 16:46:46 UTC, Dmitry Olshansky wrote:
>> 25-Mar-2013 20:43, Phil Lavoie пишет:
>>> I do believe that, in any case, this form is best:
>>> if( arr !is null && !arr.empty )
>>>
>>
>> Now write that one thousand times and count the typos.
>>
>>> Peace,
>>> Phil
>
> A thousand? !arr.empty instead of arr.length? Was that your point?
That short path better be correct path in most cases and this form is exactly in the opposite direction: treating if (arr) as if(arr.ptr)

Not to mention the following "idiom":
if( arr !is null && !arr.empty )

-- 
Dmitry Olshansky
March 25, 2013
On Monday, 25 March 2013 at 14:46:27 UTC, Steven Schveighoffer wrote:
> I would favor just changing the behavior.

That would silently break my code.
March 25, 2013
Vladimir Panteleev:

> That would silently break my code.

Right, that's why I have asked to just forbid something. The proposal is a little breaking change either way, but turning something into an error allows you to find it quickly in most cases. (But D also has the compiles __trait, that does not give an error, it just silently returns false instead of true).

On the other hand the behaviour change is not meant to happen all at once. It has to be a warning, then deprecation and then a change, along months and three or more DMD versions. So there is time to fix D code. And even if the old D code doesn't get changed, I think it's uncommon for code to rely on dynamic array to be actually (null,null) instead of being of zero length. So the total amount of breakage even for a semantic change is probably small. Considering the presence of the compiles __trait, the idea of the small behaviour change is not so bad.

So far this proposal was well received (almost everyone in the thread seems to agree to change the current behaviour. But some persons prefer to change it into an error, while others prefer a more handy semantics change. In both cases the change path will start at the same way: warning, deprecation, and then an error or a behavour change). This proposal is one of the about fifteen tiny D breaking changes that I am suggesting since years.

If Andrei Alexandrescu or Walter are reading this thread I'd like a comment on this very small proposal mostly meant to avoid certain bugs.

Bye,
bearophile
March 26, 2013
On Mon, 25 Mar 2013 17:28:51 -0400, Vladimir Panteleev <vladimir@thecybershadow.net> wrote:

> On Monday, 25 March 2013 at 14:46:27 UTC, Steven Schveighoffer wrote:
>> I would favor just changing the behavior.
>
> That would silently break my code.

It would seem incomplete not to have if(arr) work, and the way it works now is very error prone.

You would have to change your code either way.  Deprecating than reintroducing seems gratuitous to me.  Most people do not use if(arr) to check for array pointer when if(arr.ptr) is more descriptive.

How much do you use this "feature"?  Can they simply be replaced with if(arr.ptr)? or are they more of the type Jacob has with if(auto a = getArray())?  I'm trying to get a feel for how much breakage this causes, as I typically do not use that construct.

-Steve
March 26, 2013
On Tuesday, 26 March 2013 at 01:57:10 UTC, Steven Schveighoffer wrote:
> On Mon, 25 Mar 2013 17:28:51 -0400, Vladimir Panteleev <vladimir@thecybershadow.net> wrote:
>
>> On Monday, 25 March 2013 at 14:46:27 UTC, Steven Schveighoffer wrote:
>>> I would favor just changing the behavior.
>>
>> That would silently break my code.
>
> It would seem incomplete not to have if(arr) work, and the way it works now is very error prone.
>
> You would have to change your code either way.  Deprecating than reintroducing seems gratuitous to me.  Most people do not use if(arr) to check for array pointer when if(arr.ptr) is more descriptive.
>
> How much do you use this "feature"?  Can they simply be replaced with if(arr.ptr)? or are they more of the type Jacob has with if(auto a = getArray())?  I'm trying to get a feel for how much breakage this causes, as I typically do not use that construct.

No, it's nothing like that. In fact, it's very simple:

Users will download my open-source program, compile it successfully, then try using it - at which point, it will crash, produce incorrect output, or do something equally bad. Would you not agree that this is unacceptable?

Furthermore, I would have no way to automatically find all places where I would need to change my code. I would need to look at every if statement of my program and see if its behavior depends on whether the string/array is empty or null. My program is quite large, so, again, this is unacceptable.

I use this feature in the same way that anyone uses a nullable type. It's the same distinction between a pointer to struct that is null, or that is pointing to an instance containing the struct's .init. It's the same distinction between a value type T and the benefits of Nullable!T. "null" is usually used to indicate the absence of a value, as opposed to an empty value.

Although I can see how this can trip up new users of D, personally I prefer things to be just the way they are now. That said, I wouldn't be against forcing one to write "s is null", if the consensus was that this would improve D.
March 26, 2013
On Mon, 25 Mar 2013 22:21:47 -0400, Vladimir Panteleev <vladimir@thecybershadow.net> wrote:

> On Tuesday, 26 March 2013 at 01:57:10 UTC, Steven Schveighoffer wrote:
>> On Mon, 25 Mar 2013 17:28:51 -0400, Vladimir Panteleev <vladimir@thecybershadow.net> wrote:
>>
>>> On Monday, 25 March 2013 at 14:46:27 UTC, Steven Schveighoffer wrote:
>>>> I would favor just changing the behavior.
>>>
>>> That would silently break my code.
>>
>> It would seem incomplete not to have if(arr) work, and the way it works now is very error prone.
>>
>> You would have to change your code either way.  Deprecating than reintroducing seems gratuitous to me.  Most people do not use if(arr) to check for array pointer when if(arr.ptr) is more descriptive.
>>
>> How much do you use this "feature"?  Can they simply be replaced with if(arr.ptr)? or are they more of the type Jacob has with if(auto a = getArray())?  I'm trying to get a feel for how much breakage this causes, as I typically do not use that construct.
>
> No, it's nothing like that. In fact, it's very simple:
>
> Users will download my open-source program, compile it successfully, then try using it - at which point, it will crash, produce incorrect output, or do something equally bad. Would you not agree that this is unacceptable?

Well, it's unacceptable as long as the code is not updated, or you caveat it by saying it only was tested on compiler version X or earlier.  If we go through a deprecation cycle, and then reintroduce if(arr) to mean if(arr.length), then we are in the same situation as long as you haven't updated your code.

> Furthermore, I would have no way to automatically find all places where I would need to change my code. I would need to look at every if statement of my program and see if its behavior depends on whether the string/array is empty or null. My program is quite large, so, again, this is unacceptable.

That I agree is unacceptable.  We would have to take the unusual step of having a tool/compiler flag to identify the places this happens.  Asking people to review their code by hand is not what I had in mind.

> I use this feature in the same way that anyone uses a nullable type. It's the same distinction between a pointer to struct that is null, or that is pointing to an instance containing the struct's .init. It's the same distinction between a value type T and the benefits of Nullable!T. "null" is usually used to indicate the absence of a value, as opposed to an empty value.

The breaking distinction is between null and a non-null empty string.  In the cases where the code is setting a non-empty array, this is not an issue.  This is why I asked about usage.  It may turn out that you are checking for null, but really because when the value is set, it's never empty, it's not an issue.

> Although I can see how this can trip up new users of D, personally I prefer things to be just the way they are now. That said, I wouldn't be against forcing one to write "s is null", if the consensus was that this would improve D.

I understand the idea to use null as a "special" array value.  But the truth is, nulls act just like empty arrays in almost all aspects.

The critical failure I see is this:

int *p = null;
if(p == null) is equivalent to if(!p) and if(p != null) is equivalent to if(p).

But for arrays:

int[] a = null;
if(a == null) is NOT equivalent to if(!a), and if(a != null) is NOT equivalent to if(a).  This is the part that screws up so many people.  Because == does not do the same thing, we have a quirk that is silent and deadly.  It's made even worse that for MOST cases, if(!a) is equivalent to if(a == null).  So it's very easy to miss this critical distinction, and it's not at all intuitive.

-Steve
March 26, 2013
On 03/26/2013 03:21 AM, Vladimir Panteleev wrote:
> ...
>
> I use this feature in the same way that anyone uses a nullable type.
> It's the same distinction between a pointer to struct that is null, or
> that is pointing to an instance containing the struct's .init. It's the
> same distinction between a value type T and the benefits of Nullable!T.
> "null" is usually used to indicate the absence of a value, as opposed to
> an empty value.
>

It is not the same distinction. It is not like that for dynamic arrays!

void main(){ assert(null is []); }

> Although I can see how this can trip up new users of D, personally I
> prefer things to be just the way they are now. That said, I wouldn't be
> against forcing one to write "s is null", if the consensus was that this
> would improve D.

"s.ptr is null", actually
March 26, 2013
On Tuesday, 26 March 2013 at 08:15:05 UTC, Timon Gehr wrote:
> On 03/26/2013 03:21 AM, Vladimir Panteleev wrote:
>> ...
>>
>> I use this feature in the same way that anyone uses a nullable type.
>> It's the same distinction between a pointer to struct that is null, or
>> that is pointing to an instance containing the struct's .init. It's the
>> same distinction between a value type T and the benefits of Nullable!T.
>> "null" is usually used to indicate the absence of a value, as opposed to
>> an empty value.
>>
>
> It is not the same distinction. It is not like that for dynamic arrays!
>
> void main(){ assert(null is []); }

[] (the literal) has .ptr as null. That may or may not be a bug. To create a non-null empty array, you have to use something like (new uint[1])[0..0]. The "" literal does not have this problem.

>> Although I can see how this can trip up new users of D, personally I
>> prefer things to be just the way they are now. That said, I wouldn't be
>> against forcing one to write "s is null", if the consensus was that this
>> would improve D.
>
> "s.ptr is null", actually

The distinction being the case with a non-empty slice starting at address 0?
March 26, 2013
On Tuesday, 26 March 2013 at 03:58:35 UTC, Steven Schveighoffer wrote:
> Well, it's unacceptable as long as the code is not updated, or you caveat it by saying it only was tested on compiler version X or earlier.

That's horrible. What ever happened to fail-fast?

> If we go through a deprecation cycle, and then reintroduce if(arr) to mean if(arr.length), then we are in the same situation as long as you haven't updated your code.

The deprecation process takes years. This is a distinction much bigger than two consecutive compiler versions.

Also, D still sticks to the "if it looks like C, then it must work like C or fail to compile" principle. It's there for a reason, and I think the same reason applies to older versions of D.

>> Furthermore, I would have no way to automatically find all places where I would need to change my code. I would need to look at every if statement of my program and see if its behavior depends on whether the string/array is empty or null. My program is quite large, so, again, this is unacceptable.
>
> That I agree is unacceptable.  We would have to take the unusual step of having a tool/compiler flag to identify the places this happens.  Asking people to review their code by hand is not what I had in mind.

Even if you introduce a new flag, people may only catch the problem too late. A routine software upgrade (as by an OS's package manager) should not cause incorrect output that late in the cycle!

> The breaking distinction is between null and a non-null empty string.  In the cases where the code is setting a non-empty array, this is not an issue.  This is why I asked about usage.  It may turn out that you are checking for null, but really because when the value is set, it's never empty, it's not an issue.

Yes; to clarify, the program in question handles input that is often an empty string or array. In these cases, the length of the data is not important - it's just user data, regardless of its length. Its presence or absence is important.

> I understand the idea to use null as a "special" array value.  But the truth is, nulls act just like empty arrays in almost all aspects.
>
> The critical failure I see is this:
>
> int *p = null;
> if(p == null) is equivalent to if(!p) and if(p != null) is equivalent to if(p).
>
> But for arrays:
>
> int[] a = null;
> if(a == null) is NOT equivalent to if(!a), and if(a != null) is NOT equivalent to if(a).  This is the part that screws up so many people.  Because == does not do the same thing, we have a quirk that is silent and deadly.

Yes, but your argument is based on using the equality (==) operator. This operator can do special things in certain cases. When you have a C string and a D string, the == operator will do very different things, so pointing out that its behavior is different when the pointer / .ptr is null, when its behavior is also different when the pointer / .ptr is non-null, is not much of an argument.

The behavior is consistent with the identity (is) operator.

> It's made even worse that for MOST cases, if(!a) is equivalent to if(a == null).  So it's very easy to miss this critical distinction, and it's not at all intuitive.

Is it? If a was an object, that would invoke opEquals. I believe "a is null" is a more accurate equivalent for "!a".