Human unreadable documentation - the ugly seam between simple D and complex D

Shammah Chancellor via Digitalmars-d digitalmars-d at puremagic.com
Fri Mar 27 07:46:53 PDT 2015


On 2015-03-26 19:32:51 +0000, Idan Arye said:

> There is a discussion about D vs Go going on in several threads(yey for 
> multithreading!), and one thread is about an article by Gary Willoughby 
> that claims that Go is not suitable for sophisticated 
> programmers(http://forum.dlang.org/thread/mev7ll$mqr$1@digitalmars.com). 
> What's interesting about this one is the reddit comments, which turned 
> into an argument between simple languages that average programmers can 
> use and complex languages that only the top 1% of intelligent 
> programmers can use, but they can extract more out of them.
> 
> But the thing is - the world of the top programmers is not really 
> separate from that of average programmers. Professional development 
> teams can have a few top programmers and many average one, all be 
> working on the same project. Open source projects can have top 
> programmers working on the core while allowing average programmers to 
> contribute some simple features. Top programmers can write libraries 
> that can be used by average programmers.
> 
> To allow these things, top programmers and average programmers should 
> be able to work on the same language. Of course, any language that 
> average programmers can master should be easy for a top programmer to 
> master - but the thing is, we also want the top programmer to be able 
> to bring more out of the language, without limiting them by it's 
> over-simplicity. This will also benefit the average programmers, since 
> they also improve the quality of the libraries and modules they are 
> using.
> 
> This idea is nothing new, and was mentioned in the main(=biggest) 
> current D vs Go 
> thread(http://forum.dlang.org/thread/mdtago$em9$1@digitalmars.com?page=3#post-jeuhtlocousxtezoaqqh:40forum.dlang.org). 
> What I want to talk about here is the seams. The hurdles that in 
> practice make this duality harder.
> 
> Let's compare it to another duality that D(and many other languages, 
> mainly modern systems languages) promotes - the duality between 
> high-level and low-level. Between write-code-fast and write-fast-code.
> 
> The transition between high-level and low-level code in D consists by a 
> change of the things uses - which language constructs, which idioms, 
> which functions. But there aren't any visible seams. You don't need to 
> use FFI or to dynamically load a library file written in another 
> language or anything like that - you simply write the high-level parts 
> like you would write high-level code and the low-level parts like you 
> would write low-level code, and they just work together.
> 
> The duality between high-level D and low-level D is seamless. The 
> duality between simple D and complex D - not so much.
> 
> The seams here exist mainly in understanding how to use complex code 
> from simple code. Let's take std.algorithm(.*) for example. The 
> algorithms' implementations there are complex and use advanced D 
> features, but using them is very simple. Provided, of course, that you 
> know how to use them(and no - not everything that you know becomes 
> simple. I know how to solve regular differential equations, but it's 
> still very complex to do so).
> 
> The problem, as Andrei Alexandrescu pointed 
> out(http://forum.dlang.org/thread/mdtago$em9$1@digitalmars.com?page=6#post-mduv1i:242169:241:40digitalmars.com), 
> is learning how to use them. Ideally you'd want to be able to look at a 
> function's signature and learn from that how to use it. It's name and 
> return type should tell you what it does and it's argument names and 
> types should tell you what to send to it. The documentation only there 
> for a more through description and to warn you about pitfalls and edge 
> cases.
> 
> But when it comes to heavily templated functions - understanding the 
> signature is HARD. It's hard enough for the top programmers that can 
> handle the complex D features - it's much harder for the average 
> programmers that could have easily used these functions if they could 
> just understand the documentation.
> 
> Compare it, for example, to Jave. Even if a library doesn't contain a 
> single documentation comment, the auto-generated javadoc that contains 
> just the class tree and method signatures is usually enough to get an 
> idea of what's going where. In D, unless the author has provided some 
> actual examples, you are going to have a hard time trying to sort out 
> these complex templated signatures...
> 
> That's quite an hurdle to go though when wanting to use complex code 
> from simple code(or even from other complex code). That's the ugly seam 
> I'm talking about.
> 
> Now, if you are working on a big project(be it commercial or 
> open-source), you can find lot's of examples how to use these complex 
> functions, and that's probably how you'd tackle the problem. When you 
> are using some library you usually don't have that luxury - but these 
> libraries usually have the generated ddoc at their website. Of course - 
> that generated ddoc is full with complex templated signatures, so 
> that's not very helpful...
> 
> So, what can be done? Maybe the ddoc generator, instead of writing the 
> whole signature as-is, can emit a more human-readable version of it?
> 
> Let's look at the example Andrei mentioned - startsWith. Let's take a 
> look at the first overloaded signature:
> 
> uint startsWith(alias pred = "a == b", Range, Needles...)(Range 
> doesThisStart, Needles withOneOfThese) if (isInputRange!Range && 
> Needles.length > 1 && is(typeof(.startsWith!pred(doesThisStart, 
> withOneOfThese[0])) : bool) && 
> is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1..$])) : 
> uint));
> 
> Let's break it down and see what the user needs in order to use the function:
> 
> `uint` - the return type. Needed.
> `startsWith` - the function name. Needed.
> `(alias pred = "a == b",` - a template argument that the user might 
> want to supply - Needed.
> `Range, Needles...)` - template arguments that should usually be 
> inferable. The function won't work without them, but since the user 
> doesn't actually supply them - I'll mark them as not needed.
> `(Range doesThisStart, Needles withOneOfThese)` - the function's 
> arguments. Needed.
> 
> The rest are constraints that check the template arguments. They aren't 
> needed when you try to use the function - though they might be helpful 
> at figuring out why the compiler yells at you when you use it wrong.
> 
> So, if we take only the needed parts, we get this signature:
> 
> uint startsWith(alias pred = "a == b")(Range doesThisStart, Needles 
> withOneOfThese);
> 
> Well, doesn't this look much easier to grasp? Of course, it omits some 
> very critical information. It doesn't tell you what are `Range` and 
> `Needles` - you can look for these types in the docs and find nothing. 
> It also doesn't tell you that `Needles` is variadic.
> 
> Well - what's stopping us from adding this information *below* the 
> signature? What if ddoc would generate something like this:
> 
> uint startsWith(alias pred = "a == b")(Range doesThisStart, Needles... 
> withOneOfThese);
>    where:
>      Range is an inferred template argument
>      Needles is a variadic inferred template argument
>      isInputRange!Range
>      Needles.length > 1
>      is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[0])) : bool)
>      is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1..$])) : uint)
> 
> We've broken the signature into the parts required to use the function 
> and the parts required to FULLY understand the previous parts. The 
> motivation is that the second group of parts is also important, so it 
> needs to be there, but it creates a lot of unneeded noise so it 
> shouldn't be a direct part of the signature(at least not in the doc). 
> It's similar to the docs of other types used in the signature - it's 
> important to have these docs somewhere accessible, but you don't want 
> to add them in the middle of the signature because it'll make it 
> unreadable.
> 
> 
> 
> This idea, of course, is not a finally cooked proposal yet. We need a 
> way to tell ddoc which template arguments are supposed to be 
> inferred(can this always be done automatically?) and the last two 
> entries in my example are not super-trivial to grok(I can rewrite them 
> by-hand to make them super-simple - but can ddoc do it automatically? 
> and how?). The point of this thread is to start a discussion about 
> making ddoc generate documentations that are more... well... human 
> readable.

This is very good.  I consider myself well-versed in D, and able to 
write these sorts of functions.    However, reading the signatures is 
next to impossible.

-Shammah



More information about the Digitalmars-d mailing list