Should this work?

Brad Anderson eco at gnuk.net
Thu Jan 9 15:28:07 PST 2014


On Thursday, 9 January 2014 at 20:40:33 UTC, H. S. Teoh wrote:
> On Thu, Jan 09, 2014 at 06:25:33PM +0000, Brad Anderson wrote:
>> On Thursday, 9 January 2014 at 14:08:02 UTC, Manu wrote:
> [...]
>> >On a side note, am I the only one that finds
>> >std.algorithm/std.range/etc for string processing really 
>> >obtuse?  I
>> >can rarely understand the error messages, so say it's better 
>> >than STL
>> >is optimistic.
>> 
>> I absolutely hate the "does not match any template declaration"
>> error. It's extremely unhelpful for figuring out what you need 
>> to do
>> and anytime I try to do something fun with ranges I can expect 
>> to
>> see it a dozen times.
>
> Yeah, that error drives me up the wall too. I often get 
> screenfuls of
> errors, dumping 25 or so overloads of some obscure Phobos 
> internal
> function (like toImpl) as though an end-user would understand 
> any of it.
> You have to parse all the sig constraints (and boy some of them 
> are
> obscure), *understand* what they mean (which requires 
> understanding how
> Phobos works internally), and *then* try to figure out, by 
> elimination,
> which is the one that you intended to match, and why your code 
> failed to
> match it.
>
> I'm almost tempted to say that using sig constraints to 
> differentiate
> between template overloads is a bad idea. Instead, consider this
> alternative implementation of toImpl:
>
> 	template toImpl(S,T)
> 		// N.B.: no sig constraints here
> 	{
> 		static if (... /* sig constraint conditions for overload #1 
> */)
> 		{
> 			S toImpl(T t)
> 			{
> 				// implementation here
> 			}
> 		}
> 		else static if (... /* sig constraint conditions for overload 
> #2 */)
> 		{
> 			S toImpl(T t)
> 			{
> 				// implementation here
> 			}
> 		}
> 		...
> 		else // N.B.: user-readable error message
> 		{
> 			static assert(0, "Unable to convert " ~
> 				T.stringof ~ " to " ~ S.stringof);
> 		}
> 	}
>
> By putting all overloads inside a single template, we can give 
> a useful
> default message when no overloads match.
>

Interesting and there is a lot of flexibility there. It does make 
the functions a lot more verbose though for something that is 
really the compiler's job (clearly describing errors).

> Alternatively, maybe sig constraints can have an additional 
> string
> parameter that specifies a message that explains why that 
> particular
> overload was rejected. These messages are not displayed if at 
> least one
> overload matches; only if no overload matches, they will be 
> displayed
> (so that the user can at least see why each of the overloads 
> didn't
> match).
>

Each constraint would have a string? I think that would help for 
some of the more obscure constraints that aren't wrapped up in an 
eponymous template helper but I don't think it'd help with the 
problem generally because the problem is identifying which exact 
constraint failed.

Example:

     void main()
     {
       import std.algorithm, std.range;
       struct A { }
       auto a = recurrence!"n"(0).take(5).find(A());
     }

This is the error message you get:

---
/d14/f101.d(5): Error: template std.algorithm.find does not match 
any function template declaration. Candidates are:
/opt/compilers/dmd2/include/std/algorithm.d(3650):        
std.algorithm.find(alias pred = "a == b", R, E)(R haystack, E 
needle) if (isInputRange!R && 
is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
/opt/compilers/dmd2/include/std/algorithm.d(3713):        
std.algorithm.find(alias pred = "a == b", R1, R2)(R1 haystack, R2 
needle) if (isForwardRange!R1 && isForwardRange!R2 && 
is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) 
&& !isRandomAccessRange!R1)
/opt/compilers/dmd2/include/std/algorithm.d(3749):        
std.algorithm.find(alias pred = "a == b", R1, R2)(R1 haystack, R2 
needle) if (isRandomAccessRange!R1 && isBidirectionalRange!R2 && 
is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
/opt/compilers/dmd2/include/std/algorithm.d(3821):        
std.algorithm.find(alias pred = "a == b", R1, R2)(R1 haystack, R2 
needle) if (isRandomAccessRange!R1 && isForwardRange!R2 && 
!isBidirectionalRange!R2 && 
is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
/opt/compilers/dmd2/include/std/algorithm.d(4053):        
std.algorithm.find(alias pred = "a == b", Range, Ranges...)(Range 
haystack, Ranges needles) if (Ranges.length > 1 && 
is(typeof(startsWith!pred(haystack, needles))))
---

Where do you even begin with that flood of information? To fix it 
all you really want to see is which constraint you didn't 
satisfy. An error message like this would help greatly:

---
/d539/f571.d(5): Error: template std.algorithm.find call fails 
all constraints. Candidates are:
/opt/compilers/dmd2/include/std/algorithm.d:
   (3650) find(alias pred = "a == b", R, E)(R haystack, E needle):
               isInputRange!R
            && is(typeof(binaryFun!pred(haystack.front, needle)) : 
bool) <- FAILS
   (3713) find(alias pred = "a == b", R1, R2)(R1 haystack, R2 
needle):
               isForwardRange!R1
            && isForwardRange!R2 <- FAILS
            && is(typeof(binaryFun!pred(haystack.front, 
needle.front)) : bool)
            && !isRandomAccessRange!R1
   (3749) find(alias pred = "a == b", R1, R2)(R1 haystack, R2 
needle):
               isRandomAccessRange!R1 <- FAILS
            && isBidirectionalRange!R2
            && is(typeof(binaryFun!pred(haystack.front, 
needle.front)) : bool)
   (3821) find(alias pred = "a == b", R1, R2)(R1 haystack, R2 
needle)
               isRandomAccessRange!R1 <- FAILS
            && isForwardRange!R2
            && !isBidirectionalRange!R2
            && is(typeof(binaryFun!pred(haystack.front, 
needle.front)) : bool)
   (4053) find(alias pred = "a == b", Range, Ranges...)(Range 
haystack, Ranges needles)
               Ranges.length > 1 <-- FAILS
            && is(typeof(startsWith!pred(haystack, needles)))

---

The NG line limit will probably mangle that and I'm assuming 
constraints are short-circuited. The exact appearance isn't as 
important as just pointing out the failing constraints as 
strongly as you can.

>
> [...]
>> >I also find the names of the generic algorithms are often 
>> >unrelated
>> >to the name of the string operation.  My feeling is, everyone 
>> >is
>> >always on about how cool D is at string, but other than 
>> >'char[]', and
>> >the builtin slice operator, I feel really unproductive 
>> >whenever I do
>> >any heavy string manipulation in D.
>
> Really?? I find myself much more productive, because I only 
> have to
> learn one set of generic algorithms, and I can use them not 
> just for
> strings but for all sorts of other stuff that implement the 
> range API.
> Whereas in languages like C, sure you get familiar with 
> string-specific
> functions, but then when you need a similar-operating function 
> for an
> array of ints, you have to name it something else, and then 
> basically
> the same algorithm reimplemented for linked lists, called by 
> yet another
> name, etc.. Added together, it's many times more mental load 
> than just
> learning a single set of generic algorithms that work on 
> (almost)
> everything.
>
> The composability of generic algorithms also allow me to think 
> on a more
> abstract level -- instead of thinking about manipulating 
> individual
> chars, I can figure out OK, if I split the string by "," then I 
> can
> filter for the strings I'm looking for, then join them back 
> again with
> another delimiter. Since the same set of algorithms work with 
> other
> ranges too, I can apply exactly the same thought process for 
> working
> with arrays, linked lists, and other containers, without having 
> to
> remember 5 different names of essentially the same algorithm 
> but applied
> to 5 different types.
>
>
>> I actually feel a lot more productive in D than in C++ with 
>> strings.
>> Boost's string algorithms library helps fill the gap (and at 
>> least
>> you only have one place to look for documentation when you are 
>> using
>> it) but overall I prefer my experience working in D with
>> pseudo-member chains.
>
> I found that what I got out of taking the time to learn 
> std.algorithm
> and std.range was worth far more than the effort invested.
>

Agreed. Except for some hiccups and those terrible error messages 
I find std.algorithm and std.range to be a work of genius. I envy 
them every day while I'm stuck using C++ at work.

>
> T



More information about the Digitalmars-d mailing list