Rant after trying Rust a bit

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Sat Jul 25 23:54:15 PDT 2015


On Sunday, 26 July 2015 at 06:12:55 UTC, jmh530 wrote:
> I appreciate the thorough response. I think I agree with your 
> point about error messages. Nevertheless, with respect to your 
> point about a best effort to putting constraints at the 
> top-level, there might be scope for making this easier for 
> people. For instance, if there were a way to include the 
> constraints from one template in another template. From your 
> example, maybe something like
>
> auto foo(T)(T t)
>     if(template_constraints!bar && template_constraints!baz)
> {
>     ...
>     auto b = bar(t);
>     ...
>     auto c = baz(t);
>     ...
> }
>
> Ideally the template_constraints!bar would expand so that in an 
> error message the user sees what the actual constraints are 
> instead of the more nebulous template_constraints!bar. At least 
> something like this would avoid your point with respect to 
> __traits(compiles, x).

That's certainly an interesting idea, though if we're going that 
route, it might be better to simply have the compiler do that 
automatically for you, since it can see what functions are being 
called and what they're constraints are. Still, part of the 
problem is that the constraints for bar or baz may not be 
directly related to the argument to foo but rather to a result of 
operating on it. So, bar and baz's constraints can't necessarily 
be moved up into foo's constraint like that in a meaningful way. 
It would require the code that generates the arguments to bar and 
baz as well in order to make that clear. For instance, it might 
be that T needs to be an input range for the code that's directly 
in foo. However, if you do something like

auto u = t.f1().f2().f3();
auto b = bar(u);

u then needs to be a forward range to work with bar. In order for 
that to happen, t likely needs to have been a forward range, but 
if you tried to move bar's template constraint into foo's 
template constraint, it would be trying to require that u was a 
forward range - because that's what bar needs - whereas that's r 
really not what needs to be in foo's template constraint. What it 
needs is to require that t be a forward range instead of just an 
input range. The connection between the arguments the user is 
passing to the function that they're calling and the arguments to 
templated functions that are called within that function aren't 
necessarily straightforward. So, moving those sub-constraints up 
into the top-level constraint in a way that's clear to the caller 
either requires that the writer of that function do it (because 
they are able to understand how the function's argument relates 
to the requirements of the functions being called within that 
function and thus come up with the full requirements of for the 
function's argument), or it requires that enough context be given 
to the caller for them to be able to understand how what they're 
passing in is related to the call inside the template that's 
failing to compile because that function's argument doesn't pass 
its constraint.

Without having the program who's writing this function 
translating the sub-constraints into what the requirements then 
are on the function's argument and need to go in the top-level 
constraint, I don't see how we can avoid providing at least 
_some_ of the source in error messages in order to make the 
context of the failure clear. Simply shoving all of that into the 
template constraint - even automatically - is just going to get 
ugly outside of basic cases. And really, even then, what you're 
trying to do is to take the context of the failure and put it in 
the template constraint rather than just show that context along 
with the failure. The more I think about it, the harder it seems 
to be able to provide enough information to the caller without 
pretty much just showing them the source code. The compiler 
should be able to reduce how much of the source code would have 
to be shown and thus avoid forcing the user to go look at the 
templates full source, but in anything but the most basic cases, 
that source code quickly becomes required to understand what's 
going on.

If we're truly dealing with cases where the function's argument 
is simply passed on to another function call, then the sort of 
thing that you're suggesting is pretty straightforward and would 
likely work well. But there are going to be a lot of cases where 
the constraint failure isn't on the original function argument 
but on the result of passing it through other functions or on 
something that was obtained by calling one of that argument's 
member functions. And as soon as that's what's going on, 
attempting to push the sub-constraints into the top-level 
constraint either by automatically inferring them or by having 
something like template_constraints!baz is not going to work 
well, if at all.

I don't know. I think that we can come up with solutions that fix 
many of the simple cases, but I also think that a lot of the 
simple cases are where it's easiest to maintain the top-level 
template constraints with all of the sub-constraints translated 
into top-level constraints. It's the complicated cases (which are 
going to be common) where things get ugly. And I really don't see 
a good solution.

Improved error messages obviously will help, but if the code is 
complicated enough, it eventually reaches the point that the 
caller is going to need to look at the full source code to see 
what's going on and figure out what they're screwing up, so I 
don't know how far we can go with the error messages. And it's 
also those cases where it's probably going to be hardest to 
maintain the constraints. It's a tough problem.

- Jonathan M Davis


More information about the Digitalmars-d mailing list