On Friday, 26 December 2014 at 16:21:24 UTC, Andrei Alexandrescu wrote:
I thought the std.algorithm stuff is decently documented. What would be the major pain points? -- Andrei

It's fine for someone who is familiar enough with D to decipher it. Consider the documentation for sum. The first thing you see is the declaration.

auto sum(R)(R r) if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front))); auto sum(R, E)(R r, E seed) if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front)));

I promise you, if you haven't familiarized yourself enough with D's templates, template constraints and ranges, this may as well be Martian. Then in the description of the function we see this:

If ElementType!R is a floating-point type and R is a random-access range with length and slicing, then sum uses the pairwise summation algorithm.

Wtf is ElementType? Where is R defined? Searching the site for "random-access range" will eventually bear fruit, it doesn't help in understanding the whole function.

Then there's a matter of consistency. Where as sum is documented with 'R' as a type parameters, others use 'Range'. To the hypothetical noob, it's unclear if there is a difference. Little things like this add to the frustration level.

The proverbial straw that prompted my blog rant back then was to do with std.string. I wanted to split a string on a specific character. So I looked in the std.string docs for a split function. There wasn't one. There's a 'splitLines' -- I don't recall if it existed then, but it wouldn't have been what I was looking for anyway. So I searched the docs and found std.array.split. I don't recall what the docs looked like then, but now this is what we have.

pure @safe S[] split(S)(S s) if (isSomeString!S);

This above is grokkable -- it's fairly clear that S is a string and that an array of strings is returned. Then we get these next two versions:

auto split(R, E)(R r, E delim) if (isForwardRange!R && is(typeof(ElementType!R.init == E.init))); auto split(alias isTerminator, R)(R r) if (isForwardRange!R && is(typeof(unaryFun!isTerminator(r.front))));

Umm... all I want is to split a string on a specific character. What's all this mess about ElementTypes and Rs and Es and unaryFuns and....

The description, "Eagerly splits s into an array, using delim as the delimiter." suggests this is what I'm looking for. I suppose I can just pass it a string and a character and see what happens. But, that's just trial and errro. The docs don't help me understand it. I don't like using functions I don't understand. Heaven help me if I find a need for std.array.join:

ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR)) && isInputRange!R && is(Unqual!(ElementType!(ElementType!RoR)) == Unqual!(ElementType!R))); ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR)));

So the function to split a string on line breaks, which is string-specific, is in std.string. The function to split a string on whitespace, again string-specific, is in std.array. The function to split any range on any element is in std.array. Which means I have to think of strings not just as arrays, but as ranges, and have to understand that some range functions are in std.range, others in std.array and still others in std.algorithm. That means that I don't always know where to look when I want to do something I've not done before. Even if I do manage to find what I'm looking for, I then may discover that it doesn't work because I want an array, but the function returns a range and I need to convert it to an array, which means arrays aren't really ranges like I thought and...

Again, once one is familiar with ranges, template constraints and everything that ties all these modules together, the docs are very useful. It's the new user experience that sucks. Even for those with experience under their belts. I think many programmers learn new languages by hacking stuff together and looking through the reference documentation as they go. The current state of D's documentation is not conducive to that sort of learning. It requires a significant investment of time to read up on major D features in order to make sense of the docs.

The python docs have been mentioned before and I think it's a good example. In addition to the reference, there's a Beginner's Guide and a Developer's Guide. We could use that here. It would also be extremely beneficial to eliminate the wall of template constraints from the function signatures in the reference, be consistent with template type parameters (Range,R,S, and so on), and make the reference link to relevant sections of the guides. For example, it would be great to have every mention of /forward range/ or /input range/ and such link to a page on ranges. Or, if that's not practical, having a list of links at the top of the page.

"std.algorithm makes heavy use of ranges. See link-to-range-page in the developer's guide."

It would also be nice to anticipate that people looking to operate on strings would look in std.string for things that are elsewhere. Once upon a time, the std.string docs actually did have a table of links for functions that had been moved to other pages. That was convenient and shouldn't have been removed, IMO.

Anything that can be done to minimize the time required to get up to speed is going to help. And it has to be self-contained, all in the same site. It's easy to answer posts in D.learn by pointing people to TDPL, or Ali's book, or even the wiki, but it's much more productive for people to be able to easily find what they need by following links in the documentation.

Reply via email to