December 26, 2014
On 12/26/2014 9:48 AM, Manu via Digitalmars-d wrote:

>
> Ironically, the string and algorithm functions are probably the worst
> offenders, but coincidentally, there is a high chance that these are
> the first functions anyone will ever reach for, so they present a
> terrible first impression.
>

+1

When I first made the move from D1 to D2, this caused me no end of frustration. The docs were quite unhelpful in this regard. It irked me enough that I wrote a rant about it on my old blog. It doesn't bother me anymore, so I haven't thought about it in years. This post brings it back.
December 26, 2014
On 20 Dec 2014 20:40, "Atila Neves via Digitalmars-d" < digitalmars-d@puremagic.com> wrote:
>
> FWIW, I'd like to thank you for taking the time (and putting up with some
undeserved abuse for it) to write about your experience in trying to get other people to adopt D. For some they might be known issues, but the fact is these things matter, and knowing about them is part of fixing them.
>
> Alas, I abandoned doing development on Windows ages ago, so it's not
something I can help with. I don't rely on debuggers myself, but I do use them when it makes sense to me and it's annoying enough when gdb can't print things properly even with Iain's patched version.
>

We should really put all debugging issues into (separate) bug reports and tie them together with a keyword.

I use gdc compiled code for all my testing, so that is really the baseline which I'm implementing against.

I will need to know whether it is reproducible with gdc, or is another problem that needs to be fixed in dmd's dwarf-gen.

Iain.


December 26, 2014
On 12/25/14 5:18 PM, Mike Parker wrote:
> On 12/26/2014 9:48 AM, Manu via Digitalmars-d wrote:
>
>>
>> Ironically, the string and algorithm functions are probably the worst
>> offenders, but coincidentally, there is a high chance that these are
>> the first functions anyone will ever reach for, so they present a
>> terrible first impression.
>>
>
> +1
>
> When I first made the move from D1 to D2, this caused me no end of
> frustration. The docs were quite unhelpful in this regard. It irked me
> enough that I wrote a rant about it on my old blog. It doesn't bother me
> anymore, so I haven't thought about it in years. This post brings it back.

I thought the std.algorithm stuff is decently documented. What would be the major pain points? -- Andrei

December 27, 2014
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.
December 27, 2014
"Mike Parker"  wrote in message news:wwonahubwyixrseqbrpq@forum.dlang.org...

> 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)));

I agree, the signatures are basically unreadable.

I usually just skip over them and look at the examples, which most functions thankfully have.

eg for join:
const string[] arr = ["apple", "banana"];
assert(arr.join(",") == "apple,banana");
assert(arr.join() == "applebanana"); 

December 27, 2014
Daniel Murphy:

> I agree, the signatures are basically unreadable.

In Scala they have added the @usecase annotation to face this problem:
https://wiki.scala-lang.org/display/SW/Tags+and+Annotations

From that page:

@usecase <simple definition> In case the method definition is too complex, you can add simple aliasing definition as a usecase. It will create another entry in the scaladoc page as if the <simple definition> actually existed. The description for the newly created entry is the same as the one for the current symbol, just that it's preceded by [use case] to signal it's not a real entry. An example can be seen in the ++ method of scala.collections.immutable.Set.


Elsewhere there is written:

the usecases inherit the comments from their parents, such
as the explanation and the annotations: @param, @tparam, @return, etc.

Bye,
bearophile
December 27, 2014
On 12/27/2014 6:27 PM, Daniel Murphy wrote:
> "Mike Parker"  wrote in message
> news:wwonahubwyixrseqbrpq@forum.dlang.org...
>
>> 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)));
>
> I agree, the signatures are basically unreadable.
>
> I usually just skip over them and look at the examples, which most
> functions thankfully have.
>
> eg for join:
> const string[] arr = ["apple", "banana"];
> assert(arr.join(",") == "apple,banana");
> assert(arr.join() == "applebanana");

Me, too. I haven't been put off by the docs in years (has it really been *years*?). But you and I are hardly new to D :)
December 27, 2014
docs...

SortedRange!(Range, less) sort(alias less = "a < b", SwapStrategy
ss = SwapStrategy.unstable, Range)(Range r) if ((ss ==
SwapStrategy.unstable && (hasSwappableElements!Range ||
hasAssignableElements!Range) || ss != SwapStrategy.unstable &&
hasAssignableElements!Range) && isRandomAccessRange!Range &&
hasSlicing!Range && hasLength!Range);

vs code..

SortedRange!(Range, less)
sort(alias less = "a < b", SwapStrategy ss =
SwapStrategy.unstable,
         Range)(Range r)
     if (((ss == SwapStrategy.unstable &&
(hasSwappableElements!Range ||

hasAssignableElements!Range)) ||
          (ss != SwapStrategy.unstable &&
hasAssignableElements!Range)) &&
         isRandomAccessRange!Range &&
         hasSlicing!Range &&
         hasLength!Range)

Why would you make the documentation less clear than the actual
code? If somebody submitted code formatted like your docs they'd
be hung drawn and quartered.

It'd also be nice if you could click on things like SwapStrategy
and isRandomAccessRange. If you can click, read, and click back,
you can grep things so much faster than if you have to manually
look everything up.

It'd also help if there was a bit more structure conveyed in the
formating. For example the only difference between a class member
and the class is the indentation. Same font, same colours, same
underline.
December 27, 2014
On 12/27/2014 12:32 AM, Mike Parker wrote:
> 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
> [...]

Thank you for taking the time for a detailed accounting. This is very helpful.

December 27, 2014
On 2014-12-26 01:48, Manu via Digitalmars-d wrote:

> In this case, my colleague felt it was hard to understand the string
> functions, but he got through it (after sending me txt messages to ask
> questions).
> I recall making the same comments when I first started trying to do
> string manipulation in D. My personal take is, string functions are
> all templates, which makes them look more complex than they are, and
> for me, it was really hard to understand where to look for certain
> functions; they're spread fairly evenly across 5 or so different
> modules. Algorithm, range, ascii, uni, utf. Some of the most important
> functions are in range and algorithm. Perhaps those that are fairly
> string specific from range/algorithm should have aliases in
> std.string, or std.string might public import some of the minor
> modules, like ascii/uni/utf?

Don't forget about std.array as well ;)

-- 
/Jacob Carlborg