Why is std.algorithm so complicated to use?

Jonathan M Davis jmdavisProg at gmx.com
Mon Jul 16 14:16:38 PDT 2012


On Monday, July 16, 2012 22:53:36 Timon Gehr wrote:
> On 07/16/2012 05:22 PM, Don Clugston wrote:
> > On 10/07/12 16:59, Andrei Alexandrescu wrote:
> >> On 7/10/12 9:59 AM, H. S. Teoh wrote:
> >>> On Tue, Jul 10, 2012 at 09:28:51AM -0400, Andrei Alexandrescu wrote:
> >>>> On 7/10/12 2:50 AM, Jacob Carlborg wrote:
> >>>>> On 2012-07-09 22:16, Andrei Alexandrescu wrote:
> >>>>>> So foo is a range of strings, because each element of it is a
> >>>>>> string. Then you want to chain a range of strings with a string,
> >>>>>> which is a range of dchar. That doesn't work, and I agree the error
> >>>>>> message should be more informative.
> >>>>> 
> >>>>> Is that by design or something that can be fixed?
> >>>> 
> >>>> We can arrange things in the library that a custom message is issued,
> >>>> or in the compiler to do it once for all.
> >>> 
> >>> Please don't do it in the compiler. Custom messages should be in the
> >>> library. Tying the compiler to phobos is a bad idea; druntime should be
> >>> the only dependency.
> >> 
> >> The idea there being that the compiler could give good details about
> >> what part of a complex constraint has failed.
> > 
> > However the compiler doesn't know which constraint was supposed to pass.
> > If it is lucky enough to only have one template, it can do it, but if it
> > has:
> > template1 if (A && B)
> > template2 if (C && D)
> > template3 if ( (E || F) && G)
> > 
> > should it print:
> > foo.d(99): Error: no matching template
> > foo.d(10): constraint failed: A
> > foo.d(28): constraint failed: D
> > foo.d(57): constraint failed: (E || F)
> > ?
> > Could be a very long list, if there are many templates.
> > 
> > Determining the minimal list of constraints seems like a nice
> > application for BDDs...
> 
> Well, are template constraints likely to contain a lot of redundant
> information?

They're likely to contain a lot of stuff negation of other template 
constraints. For instance,

auto func(R)(R range)
    if(isForwardRange!R && !isBidirectionalRange!R)
{}

auto func(R)(R range)
    if(isBidirectionalRange!R)
{}

If you have a function with very many overloads, it can be very easy to end up 
with a bunch of different template constraints which are all slightly different. 
std.algorithm.find is a prime example of this.

But as much as it may be a bit overwhelming to print every failed constraint, 
without doing that, you _have_ to go to the source code to see what they were, 
which isn't all that great (especially if it's not in a library that you wrote 
and don't normally look at the source of - e.g. Phobos).

On the other hand, a failed instantiation of std.conv.to would print out reams 
of failed constraints...

Getting the compiler to print out the constraints come out to if combined 
could be very useful, but in some ways, it would be worse, since it would 
generally result in one big constraint with a bunch of ||s between them - 
particularly since it can't understand how the various templates involved work 
(e.g. isForwardRange vs isBidirectionalRange). I've considered taking up the 
practice of putting all template function overloads inside of a single 
template which has a constraint for them all (with each individual function 
still having its own of course). The programmer would likely do a better job 
at simplifying it than the compiler, but often you can't do that, especially 
when different overloads require drastically different stuff (e.g. the 
constraints on needle often depend on the constraints on haystack for find). 
So, in a lot of cases, you'd just end up with a really nasty looking and hard 
to understand template constraint.

If it weren't for the case of std.conv, I'd argue for just displaying all of 
the failed constraints, but I don't know.

- Jonathan M Davis


More information about the Digitalmars-d mailing list