March 21, 2012
On Wednesday, March 21, 2012 15:54:51 Daniel Murphy wrote:
> FWIW, I would rather see `if (array)` translated to `if (array.length)` and this become the recomended way to check if an array is empty. Wouldn't that remove the dependency on std.array for most of the cases?

The problem with checking whether length == 0 is that it's inefficient for some containers, so it's generally good practice to use empty rather than length. And while length == 0 is fine for arrays, it promotes bad habits in general, so I'm against it and think that code should pretty much always use empty rather than length == 0.

if(array)

is a bit different, because you're not specifically checking the length, but

if(container)

doesn't work in the general case, and stuff like

if(array || cond)

doesn't work. So, making

if(array)

be equivalent to

if(array.length != 0)

and

if(!array.empty)

rather than

if(array !is null)

may be a good idea, but it doesn't work in the general case. In the general case, you're still going to have to choose between length == 0 and empty, and I definitely think that empty is the correct choice, because it promotes good habits, whereas length == 0 promotes bad habits. So, there's value in putting empty in _object.d regardless of what happens with if.

Now, I find that I use enough other stuff in std.array, that it always gets imported anyway, but I don't think that putting empty in _object.d is a bad idea.

- Jonathan M Davis
March 21, 2012
On Wed, 21 Mar 2012 14:33:58 -0400, Jonathan M Davis <jmdavisProg@gmx.com> wrote:

> On Wednesday, March 21, 2012 15:54:51 Daniel Murphy wrote:
>> FWIW, I would rather see `if (array)` translated to `if (array.length)` and
>> this become the recomended way to check if an array is empty. Wouldn't that
>> remove the dependency on std.array for most of the cases?
>
> The problem with checking whether length == 0 is that it's inefficient for some
> containers, so it's generally good practice to use empty rather than length.

But we are specifically talking about arrays, not containers in general.  Containers in general are not defined by the language.

> And while length == 0 is fine for arrays, it promotes bad habits in general, so
> I'm against it and think that code should pretty much always use empty rather
> than length == 0.

I think you may misunderstand the proposal.  if(array) translating to if(array.length) is *only* for arrays, not for general types.

I don't see why defining empty in object.d is necessary for things that don't involve ranges at all.

Any time you import a container, it's going to import std.range, which publicly imports std.array.

Bottom line: if you don't care about ranges, if(array) works just fine (checks if length != 0).  If you care about ranges, you are going to be importing std.range and therefore std.array, and array.empty works just as well.

As an bonus this gets rid of the controversial behavior of if(array) translating to if(array.ptr).

-Steve
March 21, 2012
"Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message news:op.wbjc7au8eav7ka@localhost.localdomain...
>
> I don't see why defining empty in object.d is necessary for things that don't involve ranges at all.
>

But arrays *are* ranges. Or at least they're supposed to be.

> Any time you import a container, it's going to import std.range, which publicly imports std.array.
>

Arrays and AAs *are* containers, but you *don't* import them.

> Bottom line: if you don't care about ranges, if(array) works just fine (checks if length != 0).  If you care about ranges, you are going to be importing std.range and therefore std.array, and array.empty works just as well.
>

Yes, but a better bottom line would be:

If you want to check if something has no elements, use "empty". Period. Done.

Why should it even matter whether it's an array vs some other range? Why can't checking for empty be standardized and consistent? It's a simple and fundamental enough concept.

> As an bonus this gets rid of the controversial behavior of if(array) translating to if(array.ptr).
>

Yea, I'm not *necessarily* opposed to "if(array)" translating to "if(array.length != 0)" (athough it would break code that relies on the "null vs empty" distinction), but I don't think it's an adequate substitute for array's range-style interface being available whenever arrays are available (ie, always).


March 21, 2012
On Wed, 21 Mar 2012 16:21:21 -0400, Nick Sabalausky <a@a.a> wrote:

> "Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message
> news:op.wbjc7au8eav7ka@localhost.localdomain...
>>
>> I don't see why defining empty in object.d is necessary for things that
>> don't involve ranges at all.
>>
>
> But arrays *are* ranges. Or at least they're supposed to be.

They can be.  But the language doesn't define them as ranges, phobos does.  In order to use arrays as ranges, I have to import std.range or std.array.

>
>> Any time you import a container, it's going to import std.range, which
>> publicly imports std.array.
>>
>
> Arrays and AAs *are* containers, but you *don't* import them.

What I meant is any container that defines ranges.  In other words, any time you are going to be using ranges, you import std.range.  Which makes array.empty work.

>> Bottom line: if you don't care about ranges, if(array) works just fine
>> (checks if length != 0).  If you care about ranges, you are going to be
>> importing std.range and therefore std.array, and array.empty works just as
>> well.
>>
>
> Yes, but a better bottom line would be:
>
> If you want to check if something has no elements, use "empty". Period.
> Done.

We have defined range interface requirements.  If we want to define .empty for everything other than ranges which can contain things, we can, but it's not done already.  If we wanted to do that, then I agree array.empty should go into object.d.

>> As an bonus this gets rid of the controversial behavior of if(array)
>> translating to if(array.ptr).
>>
>
> Yea, I'm not *necessarily* opposed to "if(array)" translating to
> "if(array.length != 0)" (athough it would break code that relies on the
> "null vs empty" distinction)

We have discussed ways of introducing this in the least painful way.  I think it should happen, no matter what the pain, since it's the least obvious choice.

> but I don't think it's an adequate substitute
> for array's range-style interface being available whenever arrays are
> available (ie, always).

This goes a step further, saying all the range-style interfaces for arrays should go into object.  I certainly don't agree with that.

-Steve
March 21, 2012
On Wed, 21 Mar 2012 16:43:28 -0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> On Wed, 21 Mar 2012 16:21:21 -0400, Nick Sabalausky <a@a.a> wrote:
>
>> "Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message
>> news:op.wbjc7au8eav7ka@localhost.localdomain...
>>>
>>> As an bonus this gets rid of the controversial behavior of if(array)
>>> translating to if(array.ptr).
>>>
>>
>> Yea, I'm not *necessarily* opposed to "if(array)" translating to
>> "if(array.length != 0)" (athough it would break code that relies on the
>> "null vs empty" distinction)
>
> We have discussed ways of introducing this in the least painful way.  I think it should happen, no matter what the pain, since it's the least obvious choice.

What I mean is the *current behavior* is the least obvious choice :D

-Steve
March 21, 2012
On Wednesday, March 21, 2012 10:17:08 Jacob Carlborg wrote:
> On 2012-03-21 09:42, Nick Sabalausky wrote:
> > "Jacob Carlborg"<doob@me.com> wrote in message
> > 
> >> Sure, why not. Do we want an "any" function as well, that is the opposite?
> > 
> > I think "!array.empty" is plenty sufficient. Besides, there are other good uses of "any" that have been brought up before.
> 
> Actually I was thinking something like "any?" in Ruby:
> 
> http://www.ruby-doc.org/core-1.8.7/Enumerable.html#method-i-any-3F

So, std.algorithm.canFind then? There has been some discussion of renaming it to any (or at least the overload that just takes the predicate and the range), but canFind gives you the behavior regardless.

- Jonathan M Davis
March 21, 2012
On Wednesday, March 21, 2012 15:46:12 Steven Schveighoffer wrote:
> On Wed, 21 Mar 2012 14:33:58 -0400, Jonathan M Davis <jmdavisProg@gmx.com>
> 
> wrote:
> > On Wednesday, March 21, 2012 15:54:51 Daniel Murphy wrote:
> >> FWIW, I would rather see `if (array)` translated to `if (array.length)`
> >> and
> >> this become the recomended way to check if an array is empty. Wouldn't
> >> that
> >> remove the dependency on std.array for most of the cases?
> > 
> > The problem with checking whether length == 0 is that it's inefficient
> > for some
> > containers, so it's generally good practice to use empty rather than
> > length.
> 
> But we are specifically talking about arrays, not containers in general. Containers in general are not defined by the language.

I know that. Much point is that length == 0 is a bad thing to do in general, because it's ineffecient with some containers. The language itself is pretty much irrelevant as far as that goes. As such, I'd argue in pretty much _any_ language that using length == 0 instead of empty is _not_ a good habit to be in. Doing it with arrays will make it much more likely that you'll end up doing it with containers without thinking about it. On the other hand, if you're in the habit of _always_ using empty rather than length == 0, then you don't have the problem.

> > And while length == 0 is fine for arrays, it promotes bad habits in
> > general, so
> > I'm against it and think that code should pretty much always use empty
> > rather
> > than length == 0.
> 
> I think you may misunderstand the proposal. if(array) translating to
> if(array.length) is *only* for arrays, not for general types.

No. I understand just fine. My point is that doing length == 0 for _any_ type of container is bad, because it promotes bad habits and leads to inefficient code for containers where it _does_ matter. So, I'd argue that if you should always be using empty rather than length == 0 - even with arrays - which currently means that you're almost always importing std.array. Having empty in object.d certainly isn't necessary, but it mitigates the problem, because then you don't have to import it if all you care about is empty.

if(array) is a special case. If we want to change it to translate to if(array.length) then fine. That's all tied up in the insanity of an empty array and a null array being considered equal. But even if you want to use that syntax, it only mitigates the length == 0 vs empty issue for arrays rather than eliminating, because it only works directly in if statements and the like. The issue of choosing length == 0 vs empty still remains in many cases.

And actually, I'd personally shy away from the if(array) syntax regardless simply because of the inherent ambiguity. Even if the language clearly defines it, I believe that it's the sort of thing that programmers are likely to misunderstand - particularly since if(var) checks for null with all of the other reference types. Experienced D programmers will know, but since newbies probably won't I'd consider it a code smell.

I think that muddling null and empty was D's largest mistake with arrays and tend to think that any code that isn't explicit about it is asking for trouble if nothing else because the odds aren't low that the programmer did one thing when they meant another, because they didn't understand how null and empty arrays interact (e.g. arr == null is _always_ a bad sign IMHO).

> I don't see why defining empty in object.d is necessary for things that don't involve ranges at all.

It's not necessary, but it would be nice. empty isn't just a range thing. Containers in general use it (across many languages whether ranges are involved or not), and for the reasons that I've already given, I don't think that length == 0 should ever be used in code. So I'd argue that you should always be using empty, which means importing std.array all the time if empty isn't in object.d, even if you _aren't_ doing range-based operations.

Now, range-based operations are so common that you pretty much have to import std.range in most code anyway, so it really isn't all that much of a burden to have to import std.array or std.range to get at empty, but putting it in object.d definitely provides some benefit.

So, I'm not sure that I care all that much whether std.array.empty gets moved to object.d or not, but I'd strongly argue that code should import std.array and use it rather than checking length == 0 as long as empty is in std.array rather than in object.d. So, if putting empty in object.d promotes the use of empty over length == 0, I'm all for it.

- Jonathan M Davis
March 21, 2012
On 2012-03-21 21:49, Jonathan M Davis wrote:
> On Wednesday, March 21, 2012 10:17:08 Jacob Carlborg wrote:
>> http://www.ruby-doc.org/core-1.8.7/Enumerable.html#method-i-any-3F
>
> So, std.algorithm.canFind then? There has been some discussion of renaming it
> to any (or at least the overload that just takes the predicate and the range),
> but canFind gives you the behavior regardless.
>
> - Jonathan M Davis

Yes, I didn't see the overload. But the Ruby version has a default predicate, that does this:

canFind!("a")([3, 4, 5]);

I'm usually using "any?" to check if an array contains any values.

-- 
/Jacob Carlborg
March 21, 2012
On Wed, 21 Mar 2012 22:30:17 +0100, Jacob Carlborg <doob@me.com> wrote:

> On 2012-03-21 21:49, Jonathan M Davis wrote:
>> On Wednesday, March 21, 2012 10:17:08 Jacob Carlborg wrote:
>>> http://www.ruby-doc.org/core-1.8.7/Enumerable.html#method-i-any-3F
>>
>> So, std.algorithm.canFind then? There has been some discussion of renaming it
>> to any (or at least the overload that just takes the predicate and the range),
>> but canFind gives you the behavior regardless.
>>
>> - Jonathan M Davis
>
> Yes, I didn't see the overload. But the Ruby version has a default predicate, that does this:
>
> canFind!("a")([3, 4, 5]);
>
> I'm usually using "any?" to check if an array contains any values.
>

I hope you mean canFind!("true")([3, 4, 5]);. canFind!"a" fails for
arrays where all elements are 0.
March 21, 2012
"Jonathan M Davis" <jmdavisProg@gmx.com> wrote in message news:mailman.985.1332364578.4860.digitalmars-d@puremagic.com...
> I know that. Much point is that length == 0 is a bad thing to do in
> general,
> because it's ineffecient with some containers. The language itself is
> pretty
> much irrelevant as far as that goes. As such, I'd argue in pretty much
> _any_
> language that using length == 0 instead of empty is _not_ a good habit to
> be
> in. Doing it with arrays will make it much more likely that you'll end up
> doing it with containers without thinking about it. On the other hand, if
> you're in the habit of _always_ using empty rather than length == 0, then
> you
> don't have the problem.

Yes, .length is inefficient on some containers... but so is indexing.  That doesn't mean using indexing on arrays is a bad habit.

If you're writing general code for ranges, you are going to have to use the range interface.  But when you're writing code for arrays only, you can take advantage of the fact that indexing is O(1), length is O(1), slicing is O(1) etc.

I'm not even advocating using arr.length == 0 to check for empty.  Just use `if (!arr)`.