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.