July 04, 2013
On 7/4/13 10:58 AM, TommiT wrote:
> 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.

A null array _is_ an empty array.

> So, the problem is that [] is not what you'd intuitively expect it to be.

Is the intuition that it's an empty array that's not part of anything, presumably obtained by forging 1 into a pointer? Didn't think so.

> 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.

I also happen to think we don't quite need [].


Andrei
July 04, 2013
On Thursday, July 04, 2013 10:26:09 Andrei Alexandrescu wrote:
> 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.

True. But if [] is guaranteed to be non-null, then you have a way to explicitly create empty arrays which are non-null, and then the only ways that you end up with an array that's actually null are via its init value and by actually using null. It makes it easier to actually distinguish between null and empty in your code without accidentally getting the wrong one.

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

To distinguish between the cases where you have a value, but it's empty, and the cases where you don't actually have a value. It's the same reason why we have std.typecons.Nullable for types that don't have null.

Arrays have null, so they shouldn't need Nullable, but if you want to be able to distinguish between null and empty, then you need to be able to be certain that something is null when you mean for it to be null and not null when it's is supposed to have a value, and arrays tend to treat null and empty as almost the same thing, so it becomes easy to screw it up if you're doing anything more complicated than checking whether a function returned null or not.

- Jonathan M Davis
July 04, 2013
On 07/04/2013 07:32 PM, Andrei Alexandrescu wrote:
> 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.

Maybe someone returned [] thinking it won't be null.

int[] foo(int[] x){
    if(a) return [1,2,3];          // not null
    if(b) return [1,2];            // not null
    if(c) return [1];              // not null
    if(e) return [];               // _
    if(x !is null) return x[0..0]; // not null
    return null;                   // null
}

void bar(){
    // ...
    auto y=foo(x);
    // ...
}


> 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.
>

Please justify "obscure".

>>>> - 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.
>

You don't understand things correctly.

This would be the new behaviour:

void main(){
   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];
}

Notice the 'nice uniformity' there.


>> 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

Which two? I only see one bulb.

> - 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 []
> ...

Yah, those are IMO fairly unfair classifications, but I really don't want to get into this kind of discussion.

July 04, 2013
On 07/04/2013 08:03 PM, Andrei Alexandrescu wrote:
> On 7/4/13 10:58 AM, TommiT wrote:
>> 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.
>
> A null array _is_ an empty array.
>

Whether null is [] is the subject of this discussion.

There are multiple possibilities, such as:

1. null !is [], but [] is []
2. null !is [], and not even [] is []
3. !is(typeof(null is [])) // also, !is(typeof(null == []))
4. null is []

1 is the most useful, 2 is the most wasteful, 3 is the sanest, and 4 is what DMD/druntime implement.

The spec is loose enough to allow an implementation to behave like 1, 2 or 4 or in even other ways.


>> So, the problem is that [] is not what you'd intuitively expect it to be.
>
> Is the intuition that it's an empty array that's not part of anything,
> presumably obtained by forging 1 into a pointer? Didn't think so.
>

Why do multiple persons come up with this suggestion independently?

>> 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.
>
> I also happen to think we don't quite need [].
>

[1,2,3]
[1,2]
null
[1]
[]

Find Waldo.
July 04, 2013
On Thursday, 4 July 2013 at 18:03:02 UTC, Andrei Alexandrescu wrote:
> A null array _is_ an empty array.

Proof (I'm sorry for not using D):

#include <cstddef>
#include <iostream>

int main()
{
   int* a = nullptr;
   int length = 0;
   int offset = 7;

   bool is_empty_0 = &a[0] - &a[length];
   bool is_empty_1 = a - a;
   bool is_empty_2 = (a + offset) - (a + offset);
   bool is_empty_3 = &(a + offset)[0] - &(a + offset)[length];

   std::cout << is_empty_0 << std::endl;
   std::cout << is_empty_1 << std::endl;
   std::cout << is_empty_2 << std::endl;
   std::cout << is_empty_3 << std::endl;
}
July 04, 2013
Andrei Alexandrescu:

> I also happen to think we don't quite need [].

It's the opposite, we don't need null to represent empty dynamic arrays.

Hopefully Rust designers will avoid such point of views, they generally prefer a less sloppy design and a stronger typing.

Bye,
bearophile
July 04, 2013
04-Jul-2013 19:00, Regan Heath пишет:
> In fact, you can generalise further.
>
> The meaning of if(x) is "compare the value of x with 0" (in C, C++, .. ).
>
> The value of x for a pointer is the address to which it points.
> The value of x for a class reference is the address of the class to
> which it refers.
>
> If D's arrays are reference types,

They are not. It's a half-reference no wonder it has a bit of schizophrenia now and then.

 then IMO they should exhibit the same
> behaviour.
>

The behavior should be the most useful and since arr.length != 0 is what 99% of time a programmer wants to check.


-- 
Dmitry Olshansky
July 04, 2013
On 7/4/13 11:38 AM, bearophile wrote:
> Andrei Alexandrescu:
>
>> I also happen to think we don't quite need [].
>
> It's the opposite, we don't need null to represent empty dynamic arrays.
>
> Hopefully Rust designers will avoid such point of views, they generally
> prefer a less sloppy design and a stronger typing.

Where does the whole "stronger typing" comes in? This is poppycock. We need real arguments here.

Andrei


July 04, 2013
Andrei Alexandrescu:

> Where does the whole "stronger typing" comes in? This is poppycock. We need real arguments here.

Maybe it's a matter of definitions, for me having "null" as literal for empty array, null pointer, empty associative array, and more is more weakly typed compared to having a literal like [] usable only for empty dynamic arrays (and strings), a literal as [:] usable only for empty associative arrays, and null for pointers, class references (and little else like a Nullable).

Bye,
bearophile
July 05, 2013
On Thursday, 4 July 2013 at 14:45:57 UTC, TommiT wrote:
> On Thursday, 4 July 2013 at 13:32:25 UTC, Steven Schveighoffer wrote:
>> On Thu, 04 Jul 2013 08:52:12 -0400, Regan Heath wrote:
>>> Indeed.  IMO if(arr) should mean if(arr.ptr) .. and I thought it did.. or did this change at some point?
>>
>> No, it should mean if(arr.length).  It means if(arr.ptr) now, and this is incorrect.  [..]
>
> The meaning of if(x) for all x of nullable types has always been if(x != null) probably in all languages.

D has differentiated values and identity.