October 18, 2013
On 10/18/2013 12:50 AM, ProgrammingGhost wrote:
> How do I find out if null was passed in? As you can guess I wasn't happy
> with the current behavior.
> ...

http://forum.dlang.org/thread/rkdzdxygpflpnaznxxnl@forum.dlang.org?page=5
October 18, 2013
On 10/18/13 3:44 AM, Regan Heath wrote:
> On Fri, 18 Oct 2013 00:32:46 +0100, H. S. Teoh <hsteoh@quickfur.ath.cx>
> wrote:
>
>> On Fri, Oct 18, 2013 at 01:27:33AM +0200, Adam D. Ruppe wrote:
>>> On Thursday, 17 October 2013 at 23:12:03 UTC, ProgrammingGhost
>>> wrote:
>>> >is null still treats [] as null.
>>>
>>> blah, you're right. It will at least distinguish it from an empty
>>> slice though (like arr[$..$]). I don't think there's any way to tell
>>> [] from null except typeof(null) at all. At runtime they're both the
>>> same: no contents, so null pointer and zero length.
>>
>> I think it's a mistake to rely on the distinction between null and
>> non-null but empty arrays in D. They should be regarded as
>> implementation details that user code shouldn't depend on. If you need
>> to distinguish between arrays that are empty and arrays that are null,
>> consider using Nullable!(T[]) instead.
>
> This comes up time and again.  The use of, and ability to distinguish
> empty from null is very useful.

I disagree.

> Yes, you run the risk of things like
> null pointer exceptions etc, but we have that risk now without the
> reward of being able to distinguish these cases.
>
> Take this simple design:
>
>    string readline();
>
> This function would like to be able to:
>   - return null for EOF
>   - return [] for a blank line

That's bad API design, pure and simple. The function should e.g. return the string including the line terminator, and only return an empty (or null) string upon EOF.


Andrei

October 18, 2013
On Friday, 18 October 2013 at 15:42:56 UTC, Andrei Alexandrescu wrote:
> On 10/18/13 3:44 AM, Regan Heath wrote:
>> On Fri, 18 Oct 2013 00:32:46 +0100, H. S. Teoh <hsteoh@quickfur.ath.cx>
>> wrote:
>>
>>> On Fri, Oct 18, 2013 at 01:27:33AM +0200, Adam D. Ruppe wrote:
>>>> On Thursday, 17 October 2013 at 23:12:03 UTC, ProgrammingGhost
>>>> wrote:
>>>> >is null still treats [] as null.
>>>>
>>>> blah, you're right. It will at least distinguish it from an empty
>>>> slice though (like arr[$..$]). I don't think there's any way to tell
>>>> [] from null except typeof(null) at all. At runtime they're both the
>>>> same: no contents, so null pointer and zero length.
>>>
>>> I think it's a mistake to rely on the distinction between null and
>>> non-null but empty arrays in D. They should be regarded as
>>> implementation details that user code shouldn't depend on. If you need
>>> to distinguish between arrays that are empty and arrays that are null,
>>> consider using Nullable!(T[]) instead.
>>
>> This comes up time and again.  The use of, and ability to distinguish
>> empty from null is very useful.
>
> I disagree.
>
>> Yes, you run the risk of things like
>> null pointer exceptions etc, but we have that risk now without the
>> reward of being able to distinguish these cases.
>>
>> Take this simple design:
>>
>>   string readline();
>>
>> This function would like to be able to:
>>  - return null for EOF
>>  - return [] for a blank line
>
> That's bad API design, pure and simple. The function should e.g. return the string including the line terminator, and only return an empty (or null) string upon EOF.
>
>
> Andrei

*That's* bad API design. readln should be symmetrical to writeln, not write. And about preserving the exact representation of new lines, readln/writeln shouldn't preserve that, pure and simple.
October 18, 2013
On Friday, 18 October 2013 at 15:42:56 UTC, Andrei Alexandrescu wrote:
> That's bad API design, pure and simple. The function should e.g. return the string including the line terminator, and only return an empty (or null) string upon EOF.

I'd say it should throw upon EOF as it is pretty high-level convenience function.
October 18, 2013
On 10/18/13 9:26 AM, Max Samukha wrote:
> *That's* bad API design. readln should be symmetrical to writeln, not
> write. And about preserving the exact representation of new lines,
> readln/writeln shouldn't preserve that, pure and simple.

Fair point. I just gave one possible alternative out of many. Thing is, relying on client code to distinguish subtleties between empty and null strings is fraught with dangers.

Andrei

October 18, 2013
On Fri, Oct 18, 2013 at 06:26:05PM +0200, Max Samukha wrote:
> On Friday, 18 October 2013 at 15:42:56 UTC, Andrei Alexandrescu wrote:
> >On 10/18/13 3:44 AM, Regan Heath wrote:
[...]
> >>Take this simple design:
> >>
> >>  string readline();
> >>
> >>This function would like to be able to:
> >> - return null for EOF
> >> - return [] for a blank line
> >
> >That's bad API design, pure and simple. The function should e.g. return the string including the line terminator, and only return an empty (or null) string upon EOF.
> >
> >
> >Andrei
> 
> *That's* bad API design. readln should be symmetrical to writeln, not write. And about preserving the exact representation of new lines, readln/writeln shouldn't preserve that, pure and simple.

I agree. A better solution is to provide an eof() method (or better, .empty) that tells you when readln() will succeed, and readln() should throw upon EOF. The problem is analogous to reading from an input range: you always check whether the range is .empty before you call .front, since when the range is empty .front has no meaningful value to return. Relying on some kind of special sentinel value to represent the absence of a value is a code smell.


T

-- 
Тише едешь, дальше будешь.
October 18, 2013
On Friday, October 18, 2013 09:55:46 Andrei Alexandrescu wrote:
> On 10/18/13 9:26 AM, Max Samukha wrote:
> > *That's* bad API design. readln should be symmetrical to writeln, not write. And about preserving the exact representation of new lines, readln/writeln shouldn't preserve that, pure and simple.
> 
> Fair point. I just gave one possible alternative out of many. Thing is, relying on client code to distinguish subtleties between empty and null strings is fraught with dangers.

Yeah, but the primary reason that it's bad design is the fact that D tries to conflate null and empty instead of keeping them distinct (which is essentially the complaint that was made). Whether that's ultimately good or bad is up for debate, but the side effect is that relying on the difference between null and empty ends up being very bug-prone, whereas in other languages which don't conflate the two, it isn't problematic in the same way, and it's much more reasonable to have the API treat them differently.

- Jonathan M Davis
October 18, 2013
On Fri, Oct 18, 2013 at 01:32:58PM -0400, Jonathan M Davis wrote:
> On Friday, October 18, 2013 09:55:46 Andrei Alexandrescu wrote:
> > On 10/18/13 9:26 AM, Max Samukha wrote:
> > > *That's* bad API design. readln should be symmetrical to writeln, not write. And about preserving the exact representation of new lines, readln/writeln shouldn't preserve that, pure and simple.
> > 
> > Fair point. I just gave one possible alternative out of many. Thing is, relying on client code to distinguish subtleties between empty and null strings is fraught with dangers.
> 
> Yeah, but the primary reason that it's bad design is the fact that D tries to conflate null and empty instead of keeping them distinct (which is essentially the complaint that was made). Whether that's ultimately good or bad is up for debate, but the side effect is that relying on the difference between null and empty ends up being very bug-prone, whereas in other languages which don't conflate the two, it isn't problematic in the same way, and it's much more reasonable to have the API treat them differently.
[...]

IMO, distinguishing between null and empty arrays is bad abstraction.  I agree with D's "conflation" of null with empty, actually. Conceptually speaking, an array is a sequence of values of non-negative length. An array with non-zero length contains at least one element, and is therefore non-empty, whereas an array with zero length is empty. Same thing goes with a slice. A slice is a view into zero or more array elements. A slice with zero length is empty, and a slice with non-zero length contains at least one element. There's nowhere in this conceptual scheme for such a thing as a "null array" that's distinct from an empty array. This distinction only crops up in implementation, and IMO leads to code smells because code should be operating based on the conceptual behaviour of arrays rather than on the implementation details.


T

-- 
The most powerful one-line C program: #include "/dev/tty" -- IOCCC
October 18, 2013
On Friday, 18 October 2013 at 15:42:56 UTC, Andrei Alexandrescu wrote:
>> This comes up time and again.  The use of, and ability to distinguish
>> empty from null is very useful.
>
> I disagree.
>

That what if does by default.
October 18, 2013
On Friday, 18 October 2013 at 16:55:19 UTC, Andrei Alexandrescu wrote:
>
> Fair point. I just gave one possible alternative out of many. Thing is, relying on client code to distinguish subtleties between empty and null strings is fraught with dangers.
>
> Andrei

I agree. Thinking about your variant of readln - it's ok to use [] as the value indicating EOF, since it is not included in the value set of type "line" as you define it. But generally, neither cast(T[])[] nor cast(T[])null should be used like that, because both of them are in the set of T[]'s values, i.e. a generic stream returning [] to signify its end would be a bad idea - that should be either a side effect or a value outside T[]'s set.

Hm, I've just said nothing with many words. Never mind.