December 14, 2015
On 12/14/15 8:50 PM, tcak wrote:
> Hiding conditionals does not seem like to be solution. Here is my idea:
>
> Show the function:
> bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2)
>
> Then show the conditions separately:
> if(
>    isInputRange!Range1 &&
>    isInputRange!Range2 &&
>    !isInfinite!Range1 &&
>    !isInfinite!Range2
> )

Here are all the overloads of find (each with differing documentation):

InputRange find(alias pred = "a == b", InputRange, Element)(InputRange haystack, Element needle) if (isInputRange!InputRange && is(typeof(binaryFun!pred(haystack.front, needle)) : bool));
InputRange find(alias pred, InputRange)(InputRange haystack) if (isInputRange!InputRange);
R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2 && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) && !isRandomAccessRange!R1);
Tuple!(Range, size_t) find(alias pred = "a == b", Range, Ranges...)(Range haystack, Ranges needles) if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles))));
Range1 find(Range1, alias pred, Range2)(Range1 haystack, BoyerMooreFinder!(pred, Range2) needle);

If you can decipher this into what find actually will accept, then you have more patience than me. I think what I wanted (it's difficult to remember) is whether I could pass in a subrange for find to search for as the needle. It appears to reject cases where the haystack is a random access range and the needle is a range (but it does work).

My take is that the user only needs to know that if he wants to find a needle in a haystack, he does:

auto result = find(haystack, needle [, needle2, ...]);

Find will handle just about any call you can think of. How can the docs just say this concisely?

-Steve
December 14, 2015
On 12/14/15 9:34 PM, Steven Schveighoffer wrote:
>
> InputRange find(alias pred = "a == b", InputRange, Element)(InputRange
> haystack, Element needle) if (isInputRange!InputRange &&
> is(typeof(binaryFun!pred(haystack.front, needle)) : bool));
> InputRange find(alias pred, InputRange)(InputRange haystack) if
> (isInputRange!InputRange);
> R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if
> (isForwardRange!R1 && isForwardRange!R2 &&
> is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) &&
> !isRandomAccessRange!R1);
> Tuple!(Range, size_t) find(alias pred = "a == b", Range,
> Ranges...)(Range haystack, Ranges needles) if (Ranges.length > 1 &&
> is(typeof(startsWith!pred(haystack, needles))));
> Range1 find(Range1, alias pred, Range2)(Range1 haystack,
> BoyerMooreFinder!(pred, Range2) needle);
>
> If you can decipher this into what find actually will accept, then you
> have more patience than me. I think what I wanted (it's difficult to
> remember) is whether I could pass in a subrange for find to search for
> as the needle. It appears to reject cases where the haystack is a random
> access range and the needle is a range (but it does work).

Heh, looking at the source, I see I was a victim of improper documentation.

There are 2 additional overloads of find that are NOT DOCUMENTED.

It's likely that the 2 overloads vary only by constraints, and that's why they are not documented.

But as a user, again, I shouldn't have to care what the constraints are specifically.

-Steve
December 15, 2015
On Tuesday, 15 December 2015 at 02:34:01 UTC, Steven Schveighoffer wrote:
> Find will handle just about any call you can think of. How can the docs just say this concisely?
>
> -Steve

I think it's an interesting question to what extent template constraints are actually suitable at all as human documentation. Should treat them as part of the source and hide them? I mean, the documentation doesn't include in and out contract conditions of functions, which I think are quite close to what template constraints are: important information for the compiler, somewhat important information for special uses, and mostly  ignorable in day to day use. I find many template constraints to be obvious; I know I can't ask for the length of an infinite range (the library writer is just letting the compiler know). Therefore, giving them prominence in the documentation often makes a lot of noise.

There's a spectrum of things from the source code we include in documentation. The body of the function, no. Argument and return types, yes. Where do template constraints fit into this spectrum? If we allow for prose documentation of template constraints, should the implementation still be displayed? In how many cases is there a clear information gain?
December 14, 2015
On 12/14/15 4:11 PM, H. S. Teoh via Digitalmars-d wrote:
> On Mon, Dec 14, 2015 at 08:53:53PM +0000, Andrei Alexandrescu via Digitalmars-d wrote:
>> One thing we definitely need to do is make the template constraints
>> rendered better in documentation, and also allow user content in them.
>> We've discussed this several times but no clear path emerged so far.
>> Maybe a strong champion would come forward?
>>
>> I'm thinking along the lines of:
> [...]
>
> This has already been filed since a long time ago:
>
> 	https://issues.dlang.org/show_bug.cgi?id=13676
>
> It just needs somebody to actually implement it. AFAICT it shouldn't be
> too hard, as ddoc.d already has most of the necessary machinery for it.

I preapproved the issue. -- Andrei

December 14, 2015
On 12/14/15 8:10 PM, Chris Wright wrote:
> version(TangoDoc) {
>    /** Documentation comment. */
>    bool isSameLangth(Range1, Range2)(Range1 r1, Range2 r2) {
>      return true;
>    }
> } else {
>    bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2)
>      if (
>        isInputRange!Range1 &&
>        isInputRange!Range2 &&
>        !isInfinite!Range1 &&
>        !isInfinite!Range2) {
>      // actual implementation
>    }
> }

We use this pattern in only a couple of places in Phobos, but I think we should generally improve the language to use less, not more, of it.

BTW I think all overloads of a given function should be under the same DDOC entry with nice descriptions of what cases they apply to. The situation right now with many function having separately-documented overloads with "Jump to: 2" etc. is undesirable.


Andrei

December 14, 2015
On 12/14/15 9:34 PM, Steven Schveighoffer wrote:
> On 12/14/15 8:50 PM, tcak wrote:
>> Hiding conditionals does not seem like to be solution. Here is my idea:
>>
>> Show the function:
>> bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2)
>>
>> Then show the conditions separately:
>> if(
>>    isInputRange!Range1 &&
>>    isInputRange!Range2 &&
>>    !isInfinite!Range1 &&
>>    !isInfinite!Range2
>> )
>
> Here are all the overloads of find (each with differing documentation):
>
> InputRange find(alias pred = "a == b", InputRange, Element)(InputRange
> haystack, Element needle) if (isInputRange!InputRange &&
> is(typeof(binaryFun!pred(haystack.front, needle)) : bool));
> InputRange find(alias pred, InputRange)(InputRange haystack) if
> (isInputRange!InputRange);
> R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if
> (isForwardRange!R1 && isForwardRange!R2 &&
> is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) &&
> !isRandomAccessRange!R1);
> Tuple!(Range, size_t) find(alias pred = "a == b", Range,
> Ranges...)(Range haystack, Ranges needles) if (Ranges.length > 1 &&
> is(typeof(startsWith!pred(haystack, needles))));
> Range1 find(Range1, alias pred, Range2)(Range1 haystack,
> BoyerMooreFinder!(pred, Range2) needle);
>
> If you can decipher this into what find actually will accept, then you
> have more patience than me. I think what I wanted (it's difficult to
> remember) is whether I could pass in a subrange for find to search for
> as the needle. It appears to reject cases where the haystack is a random
> access range and the needle is a range (but it does work).
>
> My take is that the user only needs to know that if he wants to find a
> needle in a haystack, he does:
>
> auto result = find(haystack, needle [, needle2, ...]);
>
> Find will handle just about any call you can think of. How can the docs
> just say this concisely?

This function (immensely useful, very general) is a very good archetype for thinking how to structure documentation properly. One good start would be to deconstruct the would-be documentation into components. Where do we want to list the overloads? How about the template parameters? The constraints? Examples? Free text?

A mock-up of "The Great Page for find()" would be useful.

Andrei


December 15, 2015
On Tuesday, 15 December 2015 at 03:47:30 UTC, Andrei Alexandrescu wrote:
> We use this pattern in only a couple of places in Phobos, but I think we should generally improve the language to use less, not more, of it.
>
> BTW I think all overloads of a given function should be under the same DDOC entry with nice descriptions of what cases they apply to. The situation right now with many function having separately-documented overloads with "Jump to: 2" etc. is undesirable.
>
>
> Andrei

One possible trick is to use multiple `Params:` sections. Optional parameters can be described as such in the parameter description to reduce the number of `Params:` sections needed.

Another thing we should do is simplify our overload sets/template constraints. For example, `find` has two overloads for needle search which can be collapsed into one. They have different template constraints - but only because of practical limitations in constraining all the needles properly, which should be remedied with improvements to std.traits and std.meta.

December 15, 2015
On Monday, 14 December 2015 at 20:25:17 UTC, bachmeier wrote:
> On Monday, 14 December 2015 at 19:38:26 UTC, Jack Stouffer wrote:
>
>> If you're trying to use Phobos without knowing what template constraints and ranges are, you're going to have a bad time.
>
> D is doomed if new users have to understand template constraints and ranges to use the standard library. To be honest, I don't know why you should have to understand either of those to test if two arrays have the same length.

I agree.  I was just debugging one of the phobos tests on Android/ARM and I couldn't make head nor tails of what the function was supposed to do, after a couple minutes' skim:

http://dlang.org/phobos/std_algorithm_sorting.html#nextEvenPermutation

That may be an obscure function that requires technical knowledge to use, but any function should first have a layman's description, in case the layman might want to use it.  Template constraints and ranges in the Phobos docs definitely suffer from this.  Saying that users should always read the relevant sections of Ali's book first is not going to work, as most users don't RTFM.

The problem is that documentation for an OSS project, particularly breaking it down for someone new to the language, is a classic tragedy of the commons: work that nobody benefits personally from and isn't very interesting to do, so I understand why it's the way it is.

I wonder if a possible solution is to automatically generate some doc comments and links to glossary/tutorials from the template constraints, in addition to the formatting changes others have suggested.
December 15, 2015
On Tuesday, 15 December 2015 at 02:39:16 UTC, Steven Schveighoffer wrote:
> On 12/14/15 9:34 PM, Steven Schveighoffer wrote:
>>
>> InputRange find(alias pred = "a == b", InputRange, Element)(InputRange
>> haystack, Element needle) if (isInputRange!InputRange &&
>> is(typeof(binaryFun!pred(haystack.front, needle)) : bool));
>> InputRange find(alias pred, InputRange)(InputRange haystack) if
>> (isInputRange!InputRange);
>> R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if
>> (isForwardRange!R1 && isForwardRange!R2 &&
>> is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) &&
>> !isRandomAccessRange!R1);
>> Tuple!(Range, size_t) find(alias pred = "a == b", Range,
>> Ranges...)(Range haystack, Ranges needles) if (Ranges.length > 1 &&
>> is(typeof(startsWith!pred(haystack, needles))));
>> Range1 find(Range1, alias pred, Range2)(Range1 haystack,
>> BoyerMooreFinder!(pred, Range2) needle);
>>
>> If you can decipher this into what find actually will accept, then you
>> have more patience than me. I think what I wanted (it's difficult to
>> remember) is whether I could pass in a subrange for find to search for
>> as the needle. It appears to reject cases where the haystack is a random
>> access range and the needle is a range (but it does work).
>
> Heh, looking at the source, I see I was a victim of improper documentation.
>
> There are 2 additional overloads of find that are NOT DOCUMENTED.
>
> It's likely that the 2 overloads vary only by constraints, and that's why they are not documented.
>
> But as a user, again, I shouldn't have to care what the constraints are specifically.
>
> -Steve

I started exploring D a couple of months ago and I was the original poster in the reddit thread who sparked this discussion. While on the topic of documentation I wanted to quickly put in my thoughts about what frustrated me with the documentation:

- I didn't understand the visual hierarchy for templates at first. e.g. canFind: https://dlang.org/phobos/std_algorithm_searching.html#canFind. I don't remember the exact examples but I had to look at the source code to understand what this was trying to show me

- Runnable examples would be *so* useful! They're present on the homepage, why can't the examples provided in the inline docs be runnable as well?

- Linking types, functions, etc. in the function signatures would be great *if possible*. e.g.:

bool startsWith(alias pred = "a == b", R1, R2)(R1 doesThisStart, R2 withThis) if (isInputRange!R1 && isInputRange!R2 && is(typeof(binaryFun!pred(doesThisStart.front, withThis.front)) : bool));

Make bool, isInputRange, typeof, and binaryFun all links to their definitions. If I don't know what "isInputRange" does, do I really have to go spend more time to figure out where it lives?

- Add another line break between overloaded methods! Having no separation between lines makes things very difficult to sort out

Here's an imgur album I created to show some suggestions for function signatures: http://imgur.com/a/njHKI

- Dead links = compile error when compiling documentation? This was annoying when trying to find out more about OutputRanges. The only resource I found was on the std.range page in the tee definition, and the link was dead: http://dlang.org/phobos/std_range.html#.tee

- Link to where the code is implemented in Phobos at the time of compilation. e.g. clicking the actual name of "find" could link me here: https://github.com/D-Programming-Language/phobos/blob/b6a61d9e719a9d680936db44b98fbb5dd28bf6b1/std/algorithm/searching.d#L1498. More than once I found that reading the code was more useful than reading the documentation.

I find that I learn best by reading documentation. Go's documentation is phenomenal and it's very easy to follow and understand (yes, I realize this is kind of comparing apples to oranges in terms of complexity). I don't want to have to have to read documentation on understanding the documentation.

I'm currently on winter break from university and would love to help contribute some of these changes if people think they're useful as well.
December 15, 2015
On 2015-12-15 04:51, Jakob Ovrum wrote:

> One possible trick is to use multiple `Params:` sections. Optional
> parameters can be described as such in the parameter description to
> reduce the number of `Params:` sections needed.

Yardoc, used in the Ruby world, allows to do something similar. It has the "overload" tag [1] which allows you specify multiple signatures, including parameters, in the documentation for a single method in the source code.

I should add that Ruby doesn't support overloading methods.

[1] http://www.rubydoc.info/gems/yard/file/docs/Tags.md#overload

-- 
/Jacob Carlborg