Now that's a DIP that could use some love

H. S. Teoh hsteoh at quickfur.ath.cx
Thu Sep 17 21:40:46 UTC 2020


On Thu, Sep 17, 2020 at 04:28:21PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:
> On 9/17/20 3:58 PM, H. S. Teoh wrote:
[...]
> > Exactly, this breaks DRY.
> 
> Not at all! What in the world...? The constraint is the mechanics, it
> often says little about the high-level requirements. Granted,
> sometimes the mechanism is simple enough to be sufficiently evocative.
> But would you really like the compiler error messages in terms of the
> LR step that failed?

A sig constraint that contains the equivalent of an LR step does not
belong in the constraint, it belongs in a static if inside the function
body.  Or at the very least, it belongs in a separately-documented
template with a descriptive name that indicates what exactly it's
testing for.

The sig constraint is meant for the outside world to read and digest.
Why do you think we write:

	auto myRangeFunc(R)(R r)
		if (isInputRange!R)
	{ ... }

instead of:

	auto myRangeFunc(R)(R r)
		if (is(typeof(R.init) == R)
		    && is(ReturnType!((R r) => r.empty) == bool)
		    && is(typeof((return ref R r) => r.front))
		    && !is(ReturnType!((R r) => r.front) == void)
		    && is(typeof((R r) => r.popFront)))
	{ ... }

?

Precisely because the latter is the mechanics, the former is the
high-level requirements.  If your sig constraints look like the latter
such that you need a string description for it, then perhaps you should
be thinking about refactoring it into a helper template with a
descriptive name instead.


> It's a funny coincidence this thread is going in parallel with a
> couple of other developments:
> 
> * `each` has literally inscrutable constraints (I say "literally"
> because their documentation is not visible)

So the problem is (1) the constraints ought to be publicly documented,
and (2) if that doesn't make sense, then .each ought to be rewritten
with a different charter, as you said yourself.  How exactly does adding
a string description to .each's sig constraints help in any way?
Wouldn't they just repeat what the names of the clauses already say?

(And if the names are not descriptive enough, that's all the more reason
to write a fuller description in the docs for the helper templates,
which in either case should be made public.)


> * `equals` also has nigh unreadable constraints, see
> https://github.com/dlang/phobos/pull/7635/files
> 
> If you (cut and) DRY that stuff, I'll eat it.

This is a typical ailment that plagues Phobos code: the sig constraints
expose implementation details that ought to be handled by static ifs
inside a common function body, instead of a bunch of needless overloads
with sig constraints that are completely irrelevant to the user.

As far as the user is concerned, all that matters to be able to use
.equal is:

1) It takes two ranges.

2) The predicate is a binary function that compares the elements of the
respective ranges.

These are the only two clauses that ought to appear in the sig
constraints.

Everything else belongs in static ifs inside the function body (with a
suitable else clause at the end that static asserts false, with a
message explaining why the argument types could not be supported).  All
that stuff about infinite ranges and forward ranges and whatever else is
in there, is implementation details that are completely irrelevant to
the user.  Why should the user care whether Phobos implements .equal as
5 different blocks of code, respectively taking input, forward,
infinite, transfinite ranges, vs. one block of code that can handle any
range?  That's the Phobos maintainers' problem, it's implementation
details that users do not care, and do not want to care, about.  Such
things do not belong in the sig constraints, but inside the function
body together with the rest of the implementation details.

Doing it this way will:

1) Make the sig constraints self-evident so that you don't need to
explain it in excruciating detail, esp. not write a string that
essentially paraphrases what the sig constraint already says;

2) Eliminate useless overloads that ought not to be part of the
user-facing API in the first place -- this will reduce the size of
template error messages by reducing the number of overloads (and their
associated text-dumps) printed by the compiler, and make the code easier
to maintain (have you ever tried to debug std.conv.to? Good luck finding
which of the 15 or so overloads is the one you're looking for).

3) Replace generic useless "could not match template" errors with a
static assert message that explains exactly why the given arguments
cannot work.  HERE is where your string message should be, not in the
sig constraints.


So here it is.  All cut and dried for you. Now will you eat it? ;-)


T

-- 
You have to expect the unexpected. -- RL


More information about the Digitalmars-d mailing list