April 13, 2009
On 2009-04-12 21:34:47 -0400, Daniel Keep <daniel.keep.lists@gmail.com> said:

> Michel Fortin wrote:
>> Which makes me think of one thing: why "isBounded" instead of plain and
>> simple "bounded"? Ranges don't respond to "isEmpty": they have "empty"
>> instead.
>> 
>> I think it is time to establish some kind of standard for naming things,
>> and then follow it. Something a little like Cocoa's Coding Guidelines
>> comes to mind:
>> 
>> <http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html>

You 
>> 
> mean http://digitalmars.com/d/1.0/dstyle.html ?  That said, the
> "Naming Conventions" section is actually about formatting names, not
> choosing them.

Nice.

Indeed, I think there should be a section about choosing proper names in that document.


> One major advantage Cocoa has over D is that argument names seem to be
> part of the function's name.  For example, this call:
> 
>   sendAction(aSelector, anObject, flag);
> 
> appears to be written like so in Objective C:
> 
>   [sendAction: aSelector to: anObject forAllCells: flag];
> 
> To be honest, there are times I almost wish we could not only name
> arguments on the caller side, but demand that they're named in the
> definition.
> 
>   void sendAction(SEL, Object to, extern bool forAllCells);
> 
>   sendAction(aSelector,
>     to: anObject,         // optional
>     forAllCells: true);   // required

It works in Cocoa because it's part of the method name (the actual method name for the above is "sendAction:to:forAllCells:").

Your idea doesn't work well with function overloading and passing function arguments using tuples. In D, the method mangled name includes the argument types to support that, so you can use an enum instead and get mostly the same result:

	enum ForCells { ALL, SELECTION }

	setAction(aSelector, anObject, ForCells.ALL);

Hum, that'd be a good pattern to add to a programming guideline document.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 13, 2009
Looking at the documentation, I found std.range.isForwardRange.  I had a look at the implementation, and it basically just checks to make sure you can do this:

> R r1;
> R r2 = r1;

How do you construct an input range which is not also a forward range? For structs, you can't prevent that second line from working, except by throwing an exception in postblit (which the test won't notice).  For classes, you have no control over direct assignment.

Here's a practical example: I'm playing with a program that will parse the output of DMD to find out module imports.  I want to expose this list of imports as a range.  However, this range is NOT resumable since you can't seek a program's captured stdout backwards; copying the file handle certainly isn't going to work...

You could probably do it by making sure the range is a struct pointing to a class which can then take its internal file handle and re-assign it to a split buffered stream... but I'm never going to use it like that, so why should I go through all that effort?

Aside from making it possible to forbid assignment (blech), it seems
like there needs to be a way to flag a range as not supporting resuming.
 The only reasonable way I can think of off the top of my head would be
something like this:

> template isResumableRange(R)
> {
>     enum bool isResumableRange = isInputRange!(R)
>         && (is(typeof(R.isResumableRange)) && R.isResumableRange)
>         && is(typeof(
>         {
>             R r1;
>             R r2 = r1;
>         }()));
> }
>
> unittest
> {
>     static assert(!isResumableRange!(int));
>     static assert(isResumableRange!(int[]));
>
>     struct A {}
>     struct B
>     {
>         void next();
>         bool empty();
>         int head();
>     }
>     struct C
>     {
>         static enum isResumableRange = true;
>         void next();
>         bool empty();
>         int head();
>     }
>     struct D
>     {
>         static enum isResumableRange = false;
>         void next();
>         bool empty();
>         int head();
>     }
>     static assert(isResumableRange!(A));
>     static assert(isResumableRange!(B));
>     static assert(isResumableRange!(C));
>     static assert(isResumableRange!(D));
> }

I also took the opportunity to rename isForwardRange to isResumableRange since I can't see how the ability to make a "checkpoint" has anything to do with being able to go forward.  For example isBidirectionalRange requires isForwardRange, but what does saving checkpoints have to do with being able to go in both directions?

  -- Daniel
April 13, 2009
Michel Fortin wrote:
> Which makes me think of one thing: why "isBounded" instead of plain and simple "bounded"? Ranges don't respond to "isEmpty": they have "empty" instead.

"bounded(x)" can be read as a predicate ("Is x bounded?"), an assertion
("x is bounded, so treat it as such.") or even a conversion (although I
admit that I can think of a meaningful way to convert an unbounded
sequence into a bounded sequence).  "isBounded(x)" is unambiguous.


-- 
Rainer Deyke - rainerd@eldwood.com
April 13, 2009
Michel Fortin wrote:
> On 2009-04-12 11:09:51 -0400, Lars Kyllingstad <public@kyllingen.NOSPAMnet> said:
> 
>> Andrei Alexandrescu wrote:
>>> Lars Kyllingstad wrote:
>>>> I think isInfinite!() should be called isInfiniteRange!(). The current name is, in my opinion, too general.
>>>
>>> I'm undecided about this (and similar cases). isInfinite sits inside std.range, so std.range.isInfinite is clear and std.range.isInfiniteRange feels redundant. On the other hand, I don't want to use too common symbols because then the user will be forced to prefix them whenever they clash.
>>
>> I'm not too worried about name clashes, I just think it sounds wrong. If R is a range with infinitely many elements, I think it's more correct to say "R is an infinite range" than to say "R is infinite".
>>
>> As an example of what I mean, let the range R be the sequence 1, 1/4, 1/9, ...:
>>
>>    alias Sequence!("1/(n*n)", 1) R
>>
>> Then, isInfiniteRange!(R) should obviously yield true. From a mathematical standpoint, I think the result of isInfinite!(R) is less obvious. Yes, the range has infinitely many elements, but none of them are infinite, nor is their sum infinite.
> 
> Perhaps it should be renamed to isUnbounded then.

...except that my example, and indeed any range produced by sequence, recurrence, etc. are bounded at one end. Thus the term "infinite range" is more precise, and fits in well with the mathematical terms "infinite series" and "infinite sequence". Just not "infinite" alone. :)

-Lars
April 13, 2009
On 2009-04-13 03:56:11 -0400, Rainer Deyke <rainerd@eldwood.com> said:

> Michel Fortin wrote:
>> Which makes me think of one thing: why "isBounded" instead of plain and
>> simple "bounded"? Ranges don't respond to "isEmpty": they have "empty"
>> instead.
> 
> "bounded(x)" can be read as a predicate ("Is x bounded?"), an assertion
> ("x is bounded, so treat it as such.") or even a conversion (although I
> admit that I can think of a meaningful way to convert an unbounded
> sequence into a bounded sequence).  "isBounded(x)" is unambiguous.

I disagree. How adding the "is" it disambiguate between the predicate and the assertion? "x.isBounded" reads more like "x is bounded" (the assertion) than "is x bounded" (the predicate).

* * *

Assuming the "is" form is less ambiguous for bounded, the same could be said about "empty". In fact, not putting "is" in front of "emtpy" is even worse since "empty" can also be a verb.

Predicate: "Is x empty?";
Assertion: "x is empty";
Verb: "empty x".

Anyway, more than advocating for or against the "is" prefix, I mostly want things to be coherent. If you use "is" for bounded, you should use "is" for empty too. That's why I'm suggesting there is a section about choosing names in Phobos' Naming Conventions.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 13, 2009
On 2009-04-13 04:20:32 -0400, Lars Kyllingstad <public@kyllingen.NOSPAMnet> said:

> ...except that my example, and indeed any range produced by sequence, recurrence, etc. are bounded at one end. Thus the term "infinite range" is more precise, and fits in well with the mathematical terms "infinite series" and "infinite sequence". Just not "infinite" alone. :)

Indeed.

And since the thing in the argument is indeed a range, I'd use infinite alone in the function name. That way, if you build something which isn't a range but still represent a sequence, it can have this part of the interface in common.

In a language that doesn't support overloading (such as C or Objective-C), I'd be in favor of keeping the "range" suffix to differenciate the function from other functions applying to other kinds of arguments. In D, or C++, which support overloading, I'm in favor of skipping the argument type suffix.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 13, 2009
Lars Kyllingstad wrote:
> Michel Fortin wrote:
>> On 2009-04-12 11:09:51 -0400, Lars Kyllingstad <public@kyllingen.NOSPAMnet> said:
>>
>>> Andrei Alexandrescu wrote:
>>>> Lars Kyllingstad wrote:
>>>>> I think isInfinite!() should be called isInfiniteRange!(). The current name is, in my opinion, too general.
>>>>
>>>> I'm undecided about this (and similar cases). isInfinite sits inside std.range, so std.range.isInfinite is clear and std.range.isInfiniteRange feels redundant. On the other hand, I don't want to use too common symbols because then the user will be forced to prefix them whenever they clash.
>>>
>>> I'm not too worried about name clashes, I just think it sounds wrong. If R is a range with infinitely many elements, I think it's more correct to say "R is an infinite range" than to say "R is infinite".
>>>
>>> As an example of what I mean, let the range R be the sequence 1, 1/4, 1/9, ...:
>>>
>>>    alias Sequence!("1/(n*n)", 1) R
>>>
>>> Then, isInfiniteRange!(R) should obviously yield true. From a mathematical standpoint, I think the result of isInfinite!(R) is less obvious. Yes, the range has infinitely many elements, but none of them are infinite, nor is their sum infinite.
>>
>> Perhaps it should be renamed to isUnbounded then.
> 
> ...except that my example, and indeed any range produced by sequence, recurrence, etc. are bounded at one end. Thus the term "infinite range" is more precise, and fits in well with the mathematical terms "infinite series" and "infinite sequence". Just not "infinite" alone. :)
> 
> -Lars

Finally! I was waiting for someone to make this point. "Bounded" would be closer to "having values within a finite interval".

Andrei
April 13, 2009
Michel Fortin wrote:
> I disagree. How adding the "is" it disambiguate between the predicate
> and the assertion? "x.isBounded" reads more like "x is bounded" (the
> assertion) than "is x bounded" (the predicate).

Method syntax usually leads to incorrect word order.  "a.b(c)", should not be read as "a does b to c", but as "Computer, do b to a and c.". It's a command to the computer, not a statement.  In that context, "isBounded(x)" reads as "Computer, is x bounded?".

I'm also in favor of "asBounded(x)" (treat x as a bounded range) and
"toBounded(x)" (convert x to a bounded range, not really applicable for
"bounded").

> Anyway, more than advocating for or against the "is" prefix, I mostly want things to be coherent. If you use "is" for bounded, you should use "is" for empty too. That's why I'm suggesting there is a section about choosing names in Phobos' Naming Conventions.

Agreed.


-- 
Rainer Deyke - rainerd@eldwood.com
April 13, 2009
Andrei Alexandrescu wrote:
> Lars Kyllingstad wrote:
>> Michel Fortin wrote:
>>> On 2009-04-12 11:09:51 -0400, Lars Kyllingstad <public@kyllingen.NOSPAMnet> said:
>>>
>>>> Andrei Alexandrescu wrote:
>>>>> Lars Kyllingstad wrote:
>>>>>> I think isInfinite!() should be called isInfiniteRange!(). The current name is, in my opinion, too general.
>>>>>
>>>>> I'm undecided about this (and similar cases). isInfinite sits inside std.range, so std.range.isInfinite is clear and std.range.isInfiniteRange feels redundant. On the other hand, I don't want to use too common symbols because then the user will be forced to prefix them whenever they clash.
>>>>
>>>> I'm not too worried about name clashes, I just think it sounds wrong. If R is a range with infinitely many elements, I think it's more correct to say "R is an infinite range" than to say "R is infinite".
>>>>
>>>> As an example of what I mean, let the range R be the sequence 1, 1/4, 1/9, ...:
>>>>
>>>>    alias Sequence!("1/(n*n)", 1) R
>>>>
>>>> Then, isInfiniteRange!(R) should obviously yield true. From a mathematical standpoint, I think the result of isInfinite!(R) is less obvious. Yes, the range has infinitely many elements, but none of them are infinite, nor is their sum infinite.
>>>
>>> Perhaps it should be renamed to isUnbounded then.
>>
>> ...except that my example, and indeed any range produced by sequence, recurrence, etc. are bounded at one end. Thus the term "infinite range" is more precise, and fits in well with the mathematical terms "infinite series" and "infinite sequence". Just not "infinite" alone. :)
>>
>> -Lars
> 
> Finally! I was waiting for someone to make this point. "Bounded" would be closer to "having values within a finite interval".
> 
> Andrei

There's a funny thing with floating-point numbers, though.
The range [real.max, real.infinity] contains only two elements, one of which is infinity. Is that an infinite range?

It would be helpful to distinguish "contains arbitrary many elements"/"length is undefined" from "contains the point at infinity"/"includes the maximum representable element for this type".
April 13, 2009
Daniel Keep wrote:
> Looking at the documentation, I found std.range.isForwardRange.  I had a
> look at the implementation, and it basically just checks to make sure
> you can do this:
> 
>> R r1;
>> R r2 = r1;
> 
> How do you construct an input range which is not also a forward range?

In fact you can't. Thank you for opening this discussion, it's very interesting.

In the current system, an input range and a forward range cannot be distinguished statically. I wanted to, and initially I thought it's possible, but I had to concede defeat. The problem s that forward ranges also need to be copyable, otherwise using them is a pain. Then I considered defining different symbols for input vs. other ranges, but that only makes things harder.

So right now... input range means "one pass, can't save iteration state and backtrack" and forward range means "forward pass, can save iteration state".

> Aside from making it possible to forbid assignment (blech), it seems
> like there needs to be a way to flag a range as not supporting resuming.
>  The only reasonable way I can think of off the top of my head would be
> something like this:
> 
>> template isResumableRange(R)
>> {
>>     enum bool isResumableRange = isInputRange!(R)
>>         && (is(typeof(R.isResumableRange)) && R.isResumableRange)
>>         && is(typeof(
>>         {
>>             R r1;
>>             R r2 = r1;
>>         }()));
>> }
>>
>> unittest
>> {
>>     static assert(!isResumableRange!(int));
>>     static assert(isResumableRange!(int[]));
>>
>>     struct A {}
>>     struct B
>>     {
>>         void next();
>>         bool empty();
>>         int head();
>>     }
>>     struct C
>>     {
>>         static enum isResumableRange = true;
>>         void next();
>>         bool empty();
>>         int head();
>>     }
>>     struct D
>>     {
>>         static enum isResumableRange = false;
>>         void next();
>>         bool empty();
>>         int head();
>>     }
>>     static assert(isResumableRange!(A));
>>     static assert(isResumableRange!(B));
>>     static assert(isResumableRange!(C));
>>     static assert(isResumableRange!(D));
>> }
> 
> I also took the opportunity to rename isForwardRange to isResumableRange
> since I can't see how the ability to make a "checkpoint" has anything to
> do with being able to go forward.  For example isBidirectionalRange
> requires isForwardRange, but what does saving checkpoints have to do
> with being able to go in both directions?

So essentially we're looking at a symbolic approach - a resumable range would need to advertise that. I've used that for isSorted too, and it works pretty well.

The remaining question is one of defaults - are most ranges resumable or not? I.e., should a range advertise explicitly when it's resumable or when it's *not* resumable? I think the safe approach is to go as you say: a range ain't resumable unless it explicitly claims to. So if you forget stuff, the compiler will remind you.

I'd love to hear more opinions on the topic.


Andrei