November 14, 2019
On Thursday, 14 November 2019 at 05:03:49 UTC, Jab wrote:
> With that kind of justification no new features should ever be added. Not to mention bug fixes that just cause no bugs. We should just stop making changes all together.

I just said that every feature has (hidden) cost, not that that cost is never justified.

> They probably want to, because the old way is impeding the new method, you can't use CTFE to construct a string.

That's not the point, the point is that it always starts with "but I just want X, nothing more!", and later inevitably someone wants Y too. Concretely in that case: "It can live peacefully beside your preferred design with the scopes." followed by the PR to deprecate extern(C++, identifier).

> Here's a counter example to your example: https://github.com/dlang/dmd/pull/10013

I actually think that got merged too quickly without thinking all special cases and consequences through.

> I wouldn't really say it's a new "feature". It'd just be exposing the information that is already available

Whatever you call it, it's another source of complexity as I mentioned before.

> How would you duplicate this doc:
>
> /// ditto

Put the UDA string in an enum?
```
enum string fooComment = "does foo"
/// does foo
@(fooComment) void foo(int a);
/// ditto
@(fooComment) void foo(string a);
```

It's hard to argue what the best current solution is without a concrete usecase.
If you write some functions / fields yourself, you can write some UDA strings that may be redundant with your doc comments.
If you want to expose documentation from an existing library (e.g. Phobos), it can be extracted beforehand instead.


November 14, 2019
On Thursday, 14 November 2019 at 03:17:38 UTC, Steven Schveighoffer wrote:
> This is always the argument against named parameters (you changed parameter X's name, and it broke my code).

And it is wrong there too! Doubly so, especially because parameter names are already exposed by CT reflection. (I use it on my web generator to make nice function forms, for example. But this isn't black magic only I know - see Phobos' ParameterIdentifierTuple).

You can leave names out when just interfacing with precompiled code, in which case they would not be accessible, same as a interface file without comments.

Of course, I'm also of the opinion that documentation is more important than code. If something appears in the documentation without a special warning, it is a solid part of the interface and cannot change. If not, it is undefined behavior and you rely on it at you own risk. Parameter names are documented.

> Isn't that what commit hooks are for? ;)

Most of D's ctfe stuff could be done by external build system things.... often more efficiently too... but it is still really nice to have in there.
November 14, 2019
On Thursday, 14 November 2019 at 09:54:35 UTC, Dennis wrote:
> That's not the point, the point is that it always starts with "but I just want X, nothing more!", and later inevitably someone wants Y too.

Once I have what I want, I'm not above just saying no to anyone else :P
November 14, 2019
On Thursday, 14 November 2019 at 07:50:31 UTC, Jonathan Marler wrote:
>  Up till now, D code is written assuming that parameter names are private.

Well, like I said in my last email, that assumption is wrong. Parameter names have been exposed via CT reflection since at least 2011 when I wrote my first web interface generator library with it.

They're even fairly conveniently available with Phobos <http://dpldocs.info/ParameterIdentifierTuple> and you can use this to write named param library hacks today.

call!foo(name => "bar", other_name => 23); // possible now

and of course they are always in documentation which is even more important imo.

> I'd also be careful about thinking about DDoc comments like they are "comments". DDoc comments may be called comments but they don't fit the criteria of normal comments.

Indeed.
November 14, 2019
On Thursday, 14 November 2019 at 12:38:33 UTC, Adam D. Ruppe wrote:
> On Thursday, 14 November 2019 at 07:50:31 UTC, Jonathan Marler wrote:
>>  Up till now, D code is written assuming that parameter names are private.
>
> Well, like I said in my last email, that assumption is wrong. Parameter names have been exposed via CT reflection since at least 2011 when I wrote my first web interface generator library with it.

Replying to state that Binderoo wouldn't be able to do fancy things like generate C++/C# interfaces without this functionality. If you've written D code assuming they're private, then that code is wrong.

Design by introspection. I can even tell whether a symbol in your class is an alias or not.
November 14, 2019
On 11/14/19 2:50 AM, Jonathan Marler wrote:

> That being said, Ddoc comments differ from named parameters. DDoc comments are already exposed to the compiler and external tools. Unlike parameter names, they are already written to be publicly consumed by external components.  Adding another access point via __traits is less significant since this information is already exposed to other external components. DDoc comments are also "opt-in".  You can use regular comments if you don't want to expose/maintain well-formatted metadata about your code.

You aren't exposing anything, as long as you don't include the source. You can't access the compiler's comment representation. Not only that, but the compiler doesn't do crap unless you are outputting documents. The lexer will treat ddoc comments as comments and COMPLETELY IGNORE THEM unless it is told to deal with them, and even then, all it does is scrub the comment into a nicer form (removing strings of ***** before the /, etc.). Then it's up to the document generator to figure it out. But that should be considered a separate consumer of the comments, not part of compilation.

> I'd also be careful about thinking about DDoc comments like they are "comments". DDoc comments may be called comments but they don't fit the criteria of normal comments.  They have their own format (a mini language within D) and have lexical significance to the compiler. They actually behave more like user-defined attributes rather than normal comments.  I have a feeling that if they were called something else like "DDoc Metadata" rather than comments then there wouldn't be much resistance to exposing them via __traits.

DDoc comments are not *required* to conform to anything. I can write whatever I want in a ddoc comment, and the effect is, the doc generator (when asked to) will generate documentation that includes the symbol which is associated with the comment. If you write malformed DDoc comments, it's still valid D code to the compiler.

What goes in that documentation is only going to look nice if you follow the rules, but they are not really requirements. DDoc still generates as well as it can if you don't give it well-formed syntax.

Or, you can just use your own doc generator, and leave the compiler out of it. They are just comments after all, ignored by the compiler for compiling code (so far).

The thing is, a documentation generator needs a parser (and in D's case, some semantic analysis for e.g. attribute/type inference) to generate nice docs. The fact that the compiler already does this helps when generating documentation, but generating docs is not part of compilation.

Yes, they truly are comments, and in the current state of affairs, a documentation generator that is included with the compiler can choose to do something with those comments. The code generating part is, and should remain, completely oblivious to the comment data.

I'm not going to continue to go around in circles here, comments are comments and should not affect code. I'd say the options I would agree with (and take this with the grain of salt that I have literally 0 power over what actually gets implemented) is:

1. We support fetching ddoc comment data for runtime consumption only (maybe even the JSON form)
2. We support a ddoc style that is not comments, and that can be both verified for correctness and accessed via __traits
3. Some really smart person figures out how to use the generated JSON at runtime and requires no updates from the compiler.

-Steve
November 14, 2019
On Thursday, 14 November 2019 at 07:50:31 UTC, Jonathan Marler wrote:
> On Thursday, 14 November 2019 at 03:17:38 UTC, Steven Schveighoffer wrote:
>> On 11/13/19 6:03 PM, Adam D. Ruppe wrote:
>>> On Wednesday, 13 November 2019 at 22:53:49 UTC, Steven Schveighoffer wrote:
>>>> I'm trying to save the reviewers and the maintainers. Code review shouldn't include reviewing stuff that isn't compiled.
>>> 
>>> Documentation is a key component of code review. And any code reviewer would surely be skeptical of a __trait(getDoc) that does anything other than use it for documentation.
>>
>> Where it starts becoming a problem is when users of a library say "you changed your docs, and it broke my code". This is always the argument against named parameters (you changed parameter X's name, and it broke my code).
>>
>
> I'm in the camp of having parameters names "private by default" and requiring libraries to opt-in to exposing them.  By my estimate, named parameters are only useful about 15% of the time.
>  Up till now, D code is written assuming that parameter names are private.  They're inconsistent and not well thought out because they were never part of the function interface. We could come up with naming standards and go through all the code to fix this, which would be a big effort, but it's hard to justify if only a small percentage of functions would actually benefit from named parameters.  By making them "opt-in" you can fix these things before you expose them.

That shouldn't be up to the developer of the library. If I want to use named parameters and I go look at the function and I see the author gave them horrible names. I'm probably not going to continue to use it and I'd probably find a different library to use. It's already considered bad practice to give parameters bad names. It is unfortunate that Phobos has some horrible names but it doesn't really follow a lot of best practices all together.

Making them opt-in makes the feature pointless and more convoluted. It's the user's choice whether they want to use them or not. It shouldn't be the author of a library dictating where the user can use named parameters.

I've seen library maintainers for C++ include function parameter name changes as part of the change log. Even though C++ doesn't have named parameters. Cause in reality the parameter name is already part of the interface and documentation of a function. That's some of the only information a user of the library sees when they go look at it. Developers should be giving them meaningful names, with or without named parameters. I don't think we should make a feature more convoluted because some individuals have bad naming practices in general.

November 14, 2019
On Thursday, 14 November 2019 at 09:54:35 UTC, Dennis wrote:
> On Thursday, 14 November 2019 at 05:03:49 UTC, Jab wrote:
>> With that kind of justification no new features should ever be added. Not to mention bug fixes that just cause no bugs. We should just stop making changes all together.
>
> I just said that every feature has (hidden) cost, not that that cost is never justified.

Your making it sound like it isn't justified because of those hidden costs.

>> They probably want to, because the old way is impeding the new method, you can't use CTFE to construct a string.
>
> That's not the point, the point is that it always starts with "but I just want X, nothing more!", and later inevitably someone wants Y too. Concretely in that case: "It can live peacefully beside your preferred design with the scopes." followed by the PR to deprecate extern(C++, identifier).

The whole purpose of extern(C++, "") was because extern(C++, identifier) was inadequate.

If you want to look at it one way. The extern(C++, "") syntax was only wanted because the extern(C++, identifier) was there. Should we have not added extern(C++, identifier) to D to begin with? People are going to want to make changes to things they don't like. Before extern(C++, identifer) was added people were probably asking for a way to link with C++ mangling. Should they have not been given that feature because down the line there's going to be numerous bugs with it that will need to be fixed and people will then be wanting more and more changes to it?

>> Here's a counter example to your example: https://github.com/dlang/dmd/pull/10013
>
> I actually think that got merged too quickly without thinking all special cases and consequences through.

Has there been any consequences?

>> I wouldn't really say it's a new "feature". It'd just be exposing the information that is already available
>
> Whatever you call it, it's another source of complexity as I mentioned before.
>
>> How would you duplicate this doc:
>>
>> /// ditto
>
> Put the UDA string in an enum?
> ```
> enum string fooComment = "does foo"
> /// does foo
> @(fooComment) void foo(int a);
> /// ditto
> @(fooComment) void foo(string a);
> ```
>
> It's hard to argue what the best current solution is without a concrete usecase.
> If you write some functions / fields yourself, you can write some UDA strings that may be redundant with your doc comments.
> If you want to expose documentation from an existing library (e.g. Phobos), it can be extracted beforehand instead.

Was kind of curious what that looks like with a comment from phobos, I think it's worse than I expected.


enum cmpComment = `
Performs a lexicographical comparison on two
$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives).
Iterating `r1` and `r2` in lockstep, `cmp` compares each element
`e1` of `r1` with the corresponding element `e2` in `r2`. If one
of the ranges has been finished, `cmp` returns a negative value
if `r1` has fewer elements than `r2`, a positive value if `r1`
has more elements than `r2`, and `0` if the ranges have the same
number of elements.
If the ranges are strings, `cmp` performs UTF decoding
appropriately and compares the ranges one code point at a time.
A custom predicate may be specified, in which case `cmp` performs
a three-way lexicographical comparison using `pred`. Otherwise
the elements are compared using `opCmp`.
Params:
    pred = Predicate used for comparison. Without a predicate
        specified the ordering implied by `opCmp` is used.
    r1 = The first range.
    r2 = The second range.
Returns:
    `0` if the ranges compare equal. A negative value if `r1` is a prefix of `r2` or
    the first differing element of `r1` is less than the corresponding element of `r2`
    according to `pred`. A positive value if `r2` is a prefix of `r1` or the first
    differing element of `r2` is less than the corresponding element of `r1`
    according to `pred`.
Note:
    An earlier version of the documentation incorrectly stated that `-1` is the
    only negative value returned and `1` is the only positive value returned.
    Whether that is true depends on the types being compared.`;

/**********************************
Performs a lexicographical comparison on two
$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives).
Iterating `r1` and `r2` in lockstep, `cmp` compares each element
`e1` of `r1` with the corresponding element `e2` in `r2`. If one
of the ranges has been finished, `cmp` returns a negative value
if `r1` has fewer elements than `r2`, a positive value if `r1`
has more elements than `r2`, and `0` if the ranges have the same
number of elements.
If the ranges are strings, `cmp` performs UTF decoding
appropriately and compares the ranges one code point at a time.
A custom predicate may be specified, in which case `cmp` performs
a three-way lexicographical comparison using `pred`. Otherwise
the elements are compared using `opCmp`.
Params:
    pred = Predicate used for comparison. Without a predicate
        specified the ordering implied by `opCmp` is used.
    r1 = The first range.
    r2 = The second range.
Returns:
    `0` if the ranges compare equal. A negative value if `r1` is a prefix of `r2` or
    the first differing element of `r1` is less than the corresponding element of `r2`
    according to `pred`. A positive value if `r2` is a prefix of `r1` or the first
    differing element of `r2` is less than the corresponding element of `r1`
    according to `pred`.
Note:
    An earlier version of the documentation incorrectly stated that `-1` is the
    only negative value returned and `1` is the only positive value returned.
    Whether that is true depends on the types being compared.
*/
@docComment(cmpComment) auto cmp(R1, R2)(R1 r1, R2 r2);


November 14, 2019
On Thursday, 14 November 2019 at 17:18:40 UTC, Jab wrote:
> Should we have not added extern(C++, identifier) to D to begin with?

> Should they have not been given that feature because down the line there's going to be numerous bugs with it that will need to be fixed and people will then be wanting more and more changes to it?

I haven't interfaced with C++ so I can't judge that, but from a spectator of the discussion point of view `extern(C++, "ns")` seemed like a worthy addition.
I only brought this up as an example to show how these things don't end with "just this one small addition" like Adam makes it out to be in the case of __traits(docComment).

> Was kind of curious what that looks like with a comment from phobos, I think it's worse than I expected.

So I wouldn't use that for Phobos, but export Phobos documentation to a preferred file format and use that. What would you do with the raw ddoc comment anyway, print it in the terminal as-is with $() and all? Re-implement macro parsing in CTFE?

The UDA duplication is acceptable when you are writing some small comments yourself:

```
struct WindowSettings
{
    @("The default width of the window in pixels.")
    int width = 1280; /// default window width in pixels

    @("The default height of the window in pixels.")
    int height = 720; /// default window height in pixels
}
```

If you want to make a GUI for setting the width and height based on the struct then you want to poll the UDA string to get a nice description for the user.
And I know it is tempting to want to combine the two, based on the availability of __traits(docComment):

```
struct WindowSettings
{
    int width = 1280; /// The default width of the window in pixels.
    int height = 720; /// The default height of the window in pixels.
}
```

But before you know it you deal with all kinds of issues, like the user-facing comment and the programmer-facing comment diverging, needing to strip the leading space, the formatter changing it to a multi-line comment introducing other whitespace, wanting to the comment to be generated at CTFE so you have to turn back to UDA's anyway etc.

And it may just turn out that simple > clever all along.
November 14, 2019
On Thursday, 14 November 2019 at 17:18:40 UTC, Jab wrote:
>>> Here's a counter example to your example: https://github.com/dlang/dmd/pull/10013
>>
>> I actually think that got merged too quickly without thinking all special cases and consequences through.
>
> Has there been any consequences?

I luckily haven't seen anything bad yet, though it's really new.
I would say a consequence is that it makes re-ordering module-level declarations a potentially code-breaking change, though that information was already leaked with __LINE__, __traits(allMembers) and some DMD bugs [1] so 'luckily' the programmer couldn't count on that anyway.

[1] https://issues.dlang.org/show_bug.cgi?id=9125