July 04, 2013
On 7/4/13 8:02 AM, Regan Heath wrote:
> On Thu, 04 Jul 2013 15:35:30 +0100, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> On 7/4/13 6:32 AM, Steven Schveighoffer wrote:
>>> I would not be opposed to a pull request that made [] be non-null, as
>>> long as it doesn't allocate.
>>
>> What would be the benefits?
>
> Being able to naturally specify a non-null empty array (literal) such
> that...
>
> char[] n = null;
> char[] e = [];
>
> assert(n is null)
> assert(e !is null);

And what would be the benefit of that?

Andrei

July 04, 2013
On 7/4/13 8:27 AM, Timon Gehr wrote:
> On 07/04/2013 04:35 PM, Andrei Alexandrescu wrote:
>> On 7/4/13 6:32 AM, Steven Schveighoffer wrote:
>>> I would not be opposed to a pull request that made [] be non-null, as
>>> long as it doesn't allocate.
>>
>> What would be the benefits?
>>
>> Andrei
>
> - Additional sentinel values at basically no cost.

OK, an "extra null". These can occasionally useful.

> - No accidental flawed relying on empty array is null or empty array !is
> null.
> (i.e. less nondeterminism.)

But null arrays stay, so this doesn't help with that. It may actually add confusion.

> - One thing less to discuss (this has come up before.)

That would be true if e.g. the null array disappeared. As such, this adds yet another type to the discussion.


Andrei
July 04, 2013
On 7/4/13 8:37 AM, monarch_dodra wrote:
> On Thursday, 4 July 2013 at 15:27:17 UTC, Timon Gehr wrote:
>> On 07/04/2013 04:35 PM, Andrei Alexandrescu wrote:
>>> On 7/4/13 6:32 AM, Steven Schveighoffer wrote:
>>>> I would not be opposed to a pull request that made [] be non-null, as
>>>> long as it doesn't allocate.
>>>
>>> What would be the benefits?
>>>
>>> Andrei
>>
>> - Additional sentinel values at basically no cost.
>>
>> - No accidental flawed relying on empty array is null or empty array
>> !is null.
>> (i.e. less nondeterminism.)
>>
>> - One thing less to discuss (this has come up before.)
>
> There are no benefits to making "[]" return null either. Implementation
> wise, instead of returning a void[] with "ptr == 0x0" and "length == 0",
> it could just as well return a void[] with "ptr == 0x1" and "length == 0".
>
> You'd get better behavior at no extra cost.

I'm clear on the no extra cost part, but confused about the benefits.

Andrei
July 04, 2013
On 7/4/13 8:53 AM, Andrei Alexandrescu wrote:
> On 7/4/13 8:37 AM, monarch_dodra wrote:
>> On Thursday, 4 July 2013 at 15:27:17 UTC, Timon Gehr wrote:
>>> On 07/04/2013 04:35 PM, Andrei Alexandrescu wrote:
>>>> On 7/4/13 6:32 AM, Steven Schveighoffer wrote:
>>>>> I would not be opposed to a pull request that made [] be non-null, as
>>>>> long as it doesn't allocate.
>>>>
>>>> What would be the benefits?
>>>>
>>>> Andrei
>>>
>>> - Additional sentinel values at basically no cost.
>>>
>>> - No accidental flawed relying on empty array is null or empty array
>>> !is null.
>>> (i.e. less nondeterminism.)
>>>
>>> - One thing less to discuss (this has come up before.)
>>
>> There are no benefits to making "[]" return null either. Implementation
>> wise, instead of returning a void[] with "ptr == 0x0" and "length == 0",
>> it could just as well return a void[] with "ptr == 0x1" and "length ==
>> 0".
>>
>> You'd get better behavior at no extra cost.
>
> I'm clear on the no extra cost part, but confused about the benefits.

BTW you get the no extra cost part with a sheer function:

auto emptyArray(T)() @trusted
{
    return (cast(T*) 1)[0 .. 0];
}


Andrei
July 04, 2013
On 07/04/2013 05:51 PM, Andrei Alexandrescu wrote:
> On 7/4/13 8:27 AM, Timon Gehr wrote:
>> On 07/04/2013 04:35 PM, Andrei Alexandrescu wrote:
>>> On 7/4/13 6:32 AM, Steven Schveighoffer wrote:
>>>> I would not be opposed to a pull request that made [] be non-null, as
>>>> long as it doesn't allocate.
>>>
>>> What would be the benefits?
>>>
>>> Andrei
>>
>> - Additional sentinel values at basically no cost.
>
> OK, an "extra null". These can occasionally useful.
>
>> - No accidental flawed relying on empty array is null or empty array !is
>> null.
>> (i.e. less nondeterminism.)
>
> But null arrays stay, so this doesn't help with that. It may actually
> add confusion.
>

It is more likely that 'if(array !is null) { }' is valid under the stronger semantics than under the old ones. This removes a potentially bug-prone construct, since it is easy to fall into the trap of expecting that different syntax denotes a different construct.

>> - One thing less to discuss (this has come up before.)
>
> That would be true if e.g. the null array disappeared. As such, this
> adds yet another type to the discussion.
> ...

What I mean is that there will be no reason to discuss the following atrocity any further:

void main(){ // (Compiles and runs with DMD)
   assert([1].ptr[0..0] !is null);
   assert([1][0..0] is null);
   int i=0;
   assert([1][i..i] !is null);
   assert([] is null);
   auto x=[1];
   assert(x[0..0] !is null);
   auto y=[1][i..i];
}

It's quite obvious to me what the rules are that govern whether or not an empty array is null, but why bother? It's simply not useful. Doing it always the same way makes a lot more sense than what's demonstrated above. And then, going for non-null empty arrays makes most sense to support an efficient implementation of slicing.

Furthermore let's look at it from a syntactic viewpoint.

Currently we have:

- empty arrays of the form cast(T[])null
- other empty arrays which are null, eg. []
- empty arrays which are not null, eg. [1][i..i]

Afterwards we have:
- empty arrays of the form cast(T[])null
- empty arrays which are not null, eg. [] or [1][i..i]

IMO that reduces cognitive load, even if not as much as simply getting rid of cast(T[])null.

July 04, 2013
On Thursday, July 04, 2013 08:41:47 Andrei Alexandrescu wrote:
> On 7/4/13 8:02 AM, Regan Heath wrote:
> > On Thu, 04 Jul 2013 15:35:30 +0100, Andrei Alexandrescu
> > 
> > <SeeWebsiteForEmail@erdani.org> wrote:
> >> On 7/4/13 6:32 AM, Steven Schveighoffer wrote:
> >>> I would not be opposed to a pull request that made [] be non-null, as long as it doesn't allocate.
> >> 
> >> What would be the benefits?
> > 
> > Being able to naturally specify a non-null empty array (literal) such
> > that...
> > 
> > char[] n = null;
> > char[] e = [];
> > 
> > assert(n is null)
> > assert(e !is null);
> 
> And what would be the benefit of that?

Making the distinction between null and empty cleaner. There are plenty of cases in CS in general where distinguishing between null and empty is useful, but unfortunately, the way we've gone about implementing null and empty with arrays in D tends to blur to the point that it's kind of iffy to use null as something distinct from empty. You can do it for simple stuff if you're careful (like explicitly returning null from a function), but it's very easy to end up with an array that's empty when you want null or vice versa. One prime case of this is [] vs null. [] is supposed to indicate an empty array, but it results in a null one, which not only helps blur the line between null and empty, but it makes it so that (similar to AAs), there's no clean way to simply declare an empty array. Now, because of how much empty and null gets blurred with dynamic arrays, you don't generally end up caring much about being able to declare an empty, non-null array as you do with AAs, but a lot of that is simply because it's arguably too risky to try and distinguish between null and empty with arrays even if you wanted to (because of how easy it is to get one when you wanted the other and how they act almost - but not quite - the same).

- Jonathan M Davis
July 04, 2013
On Thursday, 4 July 2013 at 12:52:14 UTC, Regan Heath wrote:
> Whether there is an allocation or not is secondary.  The primary goal is for [] to represent empty, not null.  We have null, if we want to represent null we pass null.  What we lack is a way to represent empty.
>
> So, I would say that what [] returns should be empty, and not null.
>
> Secondarily we want to avoid allocation, so .. can we not have [] return a slice of length 0 with ptr set to a global pre-allocated single byte of memory?
>
> R

Here's C++'s way of handling it (note the "unique"):
--quoting--
There is a special case for a zero-length array (N == 0). In that case, array.begin() == array.end(), which is some unique value. The effect of calling front() or back() on a zero-sized array is undefined.
--/quoting--
http://en.cppreference.com/w/cpp/container/array
July 04, 2013
On 7/4/13 9:51 AM, Jonathan M Davis wrote:
> On Thursday, July 04, 2013 08:41:47 Andrei Alexandrescu wrote:
>> On 7/4/13 8:02 AM, Regan Heath wrote:
>>> On Thu, 04 Jul 2013 15:35:30 +0100, Andrei Alexandrescu
>>>
>>> <SeeWebsiteForEmail@erdani.org>  wrote:
>>>> On 7/4/13 6:32 AM, Steven Schveighoffer wrote:
>>>>> I would not be opposed to a pull request that made [] be non-null, as
>>>>> long as it doesn't allocate.
>>>>
>>>> What would be the benefits?
>>>
>>> Being able to naturally specify a non-null empty array (literal) such
>>> that...
>>>
>>> char[] n = null;
>>> char[] e = [];
>>>
>>> assert(n is null)
>>> assert(e !is null);
>>
>> And what would be the benefit of that?
>
> Making the distinction between null and empty cleaner.

I don't see where that derives from at all.

> There are plenty of
> cases in CS in general where distinguishing between null and empty is useful,
> but unfortunately, the way we've gone about implementing null and empty with
> arrays in D tends to blur to the point that it's kind of iffy to use null as
> something distinct from empty.

The way code should do is use null on the creation side and .empty on the testing side. Making [] non-null does not change that.

> You can do it for simple stuff if you're careful
> (like explicitly returning null from a function), but it's very easy to end up
> with an array that's empty when you want null or vice versa. One prime case of
> this is [] vs null. [] is supposed to indicate an empty array, but it results
> in a null one, which not only helps blur the line between null and empty, but
> it makes it so that (similar to AAs), there's no clean way to simply declare
> an empty array.

Why do you want so much an empty array that's not null? I can't make sense of this entire argument.


Andrei
July 04, 2013
On 7/4/13 9:51 AM, Timon Gehr wrote:
> On 07/04/2013 05:51 PM, Andrei Alexandrescu wrote:
>> On 7/4/13 8:27 AM, Timon Gehr wrote:
>>> On 07/04/2013 04:35 PM, Andrei Alexandrescu wrote:
>>>> On 7/4/13 6:32 AM, Steven Schveighoffer wrote:
>>>>> I would not be opposed to a pull request that made [] be non-null, as
>>>>> long as it doesn't allocate.
>>>>
>>>> What would be the benefits?
>>>>
>>>> Andrei
>>>
>>> - Additional sentinel values at basically no cost.
>>
>> OK, an "extra null". These can occasionally useful.
>>
>>> - No accidental flawed relying on empty array is null or empty array !is
>>> null.
>>> (i.e. less nondeterminism.)
>>
>> But null arrays stay, so this doesn't help with that. It may actually
>> add confusion.
>>
>
> It is more likely that 'if(array !is null) { }' is valid under the
> stronger semantics than under the old ones. This removes a potentially
> bug-prone construct, since it is easy to fall into the trap of expecting
> that different syntax denotes a different construct.

Why? Maybe someone returned [] thinking it will be a null array. I don't see how adding an obscure "whoa, this is an empty array, but surprisingly, it's not null!" marks an increase in clarity.

>>> - One thing less to discuss (this has come up before.)
>>
>> That would be true if e.g. the null array disappeared. As such, this
>> adds yet another type to the discussion.
>> ...
>
> What I mean is that there will be no reason to discuss the following
> atrocity any further:
>
> void main(){ // (Compiles and runs with DMD)
> assert([1].ptr[0..0] !is null);
> assert([1][0..0] is null);
> int i=0;
> assert([1][i..i] !is null);
> assert([] is null);
> auto x=[1];
> assert(x[0..0] !is null);
> auto y=[1][i..i];
> }

Making [] changes exactly one line there if I understand things correctly, and does not improve anything much.

> Furthermore let's look at it from a syntactic viewpoint.
>
> Currently we have:
>
> - empty arrays of the form cast(T[])null
> - other empty arrays which are null, eg. []
> - empty arrays which are not null, eg. [1][i..i]
>
> Afterwards we have:
> - empty arrays of the form cast(T[])null
> - empty arrays which are not null, eg. [] or [1][i..i]
>
> IMO that reduces cognitive load, even if not as much as simply getting
> rid of cast(T[])null.

The way I see it is:

- we now have two literals for null arrays
- we also have the ability to create non-null but empty arrays through natural reduction and slicing of arrays

The new setup is:

- we'd have one literal for null arrays
- we'd still have the ability to create non-null but empty arrays through natural reduction and slicing of arrays
- we'd have Nosferatu in the form of []


Andrei
July 04, 2013
On Thursday, 4 July 2013 at 17:32:29 UTC, Andrei Alexandrescu wrote:
> [..] Maybe someone returned [] thinking it will be a null array. [..]

I wouldn't think that [] is null, and I suspect neither would very many other newcomers to the language. To me, the only problem with [] being null is that it doesn't look like null. It looks like an empty array. So, the problem is that [] is not what you'd intuitively expect it to be.

By the way, this must be a bug, right?

template arr(X_...)
{
    int[] arr = [X_]; // [1]
}

void main()
{
    auto a2 = arr!(1, 2);
    auto a1 = arr!(1);
    auto a0 = arr!(); // [2]
}

[1] Error: initializer must be an expression, not '()'
[2] Error: template instance main.arr!() error instantiating

...because if that's not supposed to work, then I don't see much point in having the [] literal in the language.