www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - User-defined template error messages

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
We've had this problem for a long time - template doesn't match because 
of a complex constraint, the error message is unhelpful, and the user 
doesn't know what to do.

I think this is an important missed opportunity. One of the promises of 
Design by Introspection is it raises the level of error messages from 
mere built-in canned messages to higher-level messages expressed in 
terms of problem-space entities.

A simple way to address this would be with a two-pronged proposal:

1. Allow multiple "if" constraints in a template. All "if" constraints 
must match. This means a natural way to write complex constraints is a 
conjunction of simpler constraints.

2. Extend the syntax to:

if (condition) else string

The string (which crucially can be a CT expression involving template 
parameters) is the error message that would be displayed should the 
condition be false.

When an overload set is being looked up, if no match then the string 
corresponding to the first failed condition in each overload is printed 
as the error message.

Example from std. Current sig:

InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, 
InputRange2 tgt)
if (isInputRange!InputRange1 && isInputRange!InputRange2
         && is(typeof(move(src.front, tgt.front))));

With the proposal:

InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, 
InputRange2 tgt)
if (isInputRange!InputRange1)
else InputRange1.stringof ~ " must be an input range"
if (isInputRange!InputRange2)
else InputRange2.stringof ~ " must be an input range"
if (is(typeof(move(src.front, tgt.front))))
else "Cannot move from the front of "~InputRange1.stringof~
     " to the front of "~InputRange2.stringof;

Larger but offers the benefits of beautiful error messages.


Andrei
Jan 13 2019
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
This is a great example of why I want signatures.

Having a concrete type that gives us a way to verify complex types 
clearly and without using complex CTFE or templates is a must if we want 
to help new users into D in the future.

Most importantly it needs to be baked into the type system enough that 
it allows conversion to a vtable as well as compile time verification of 
the input types implicitly. Based upon my experiments (so the type 
itself can adapt to an implementation).

The other problem is it needs some way to model internal attributes that 
are set externally (e.g. InputRange.Type), my named parameter DIP was 
based upon this requirement.

To go this direction won't be easy. So far my designs result in being 
able to do OOP in them and strangely they seem to be far more like best 
practice OOP than classes ever were. But alas it could just be the fact 
that I designed them talking ;)
Jan 13 2019
prev sibling next sibling parent luckoverthere <luckoverthere gmail.cm> writes:
On Sunday, 13 January 2019 at 18:54:52 UTC, Andrei Alexandrescu 
wrote:
 We've had this problem for a long time - template doesn't match 
 because of a complex constraint, the error message is 
 unhelpful, and the user doesn't know what to do.

 I think this is an important missed opportunity. One of the 
 promises of Design by Introspection is it raises the level of 
 error messages from mere built-in canned messages to 
 higher-level messages expressed in terms of problem-space 
 entities.

 A simple way to address this would be with a two-pronged 
 proposal:

 1. Allow multiple "if" constraints in a template. All "if" 
 constraints must match. This means a natural way to write 
 complex constraints is a conjunction of simpler constraints.

 2. Extend the syntax to:

 if (condition) else string

 The string (which crucially can be a CT expression involving 
 template parameters) is the error message that would be 
 displayed should the condition be false.

 When an overload set is being looked up, if no match then the 
 string corresponding to the first failed condition in each 
 overload is printed as the error message.

 Example from std. Current sig:

 InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, 
 InputRange2 tgt)
 if (isInputRange!InputRange1 && isInputRange!InputRange2
         && is(typeof(move(src.front, tgt.front))));

 With the proposal:

 InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, 
 InputRange2 tgt)
 if (isInputRange!InputRange1)
 else InputRange1.stringof ~ " must be an input range"
 if (isInputRange!InputRange2)
 else InputRange2.stringof ~ " must be an input range"
 if (is(typeof(move(src.front, tgt.front))))
 else "Cannot move from the front of "~InputRange1.stringof~
     " to the front of "~InputRange2.stringof;

 Larger but offers the benefits of beautiful error messages.


 Andrei
I don't really like that syntax, it changes the meaning of what if/else mean and how they behave from everything else. This makes more sense logically and is in line with how if/else functions. Though still not 100% accurate as the else should technically be with the last if statement. InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) if (!isInputRange!InputRange1) InputRange1.stringof ~ " must be an input range" if (!isInputRange!InputRange2) InputRange2.stringof ~ " must be an input range" if (!is(typeof(move(src.front, tgt.front)))) "Cannot move from the front of "~InputRange1.stringof~ " to the front of "~InputRange2.stringof; else { // implementation ... } Still this sort of syntax only really works well if the statement is expressed using &&'s but you can't reduce it if you need || logic. All of the || logic would need to be in the same if statement. Then determining which condition was false would require addition if statements to provide a proper message. We could just go with something like what is being done with asserts.
 InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, 
 InputRange2 tgt)
 if (isInputRange!InputRange1 && isInputRange!InputRange2
         && is(typeof(move(src.front, tgt.front))));
For the above case detect what part of the if statement failed. The error message would be something like <function declaration> Arguments don't match as isInputRange!TheRangeTypeUsed is false.
Jan 13 2019
prev sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Sunday, 13 January 2019 at 18:54:52 UTC, Andrei Alexandrescu 
wrote:
 We've had this problem for a long time - template doesn't match 
 because of a complex constraint, the error message is 
 unhelpful, and the user doesn't know what to do.

 [...]
I've beat you to it: https://github.com/dlang/DIPs/pull/131 and I like mine more. Syntactically it is compile-time contract programming, with `in` replaced by `if`, easier to describe that way. See that PR thread for more discussion.
Jan 13 2019
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/13/19 6:35 PM, Nicholas Wilson wrote:
 On Sunday, 13 January 2019 at 18:54:52 UTC, Andrei Alexandrescu wrote:
 We've had this problem for a long time - template doesn't match 
 because of a complex constraint, the error message is unhelpful, and 
 the user doesn't know what to do.

 [...]
I've beat you to it: https://github.com/dlang/DIPs/pull/131 and I like mine more. Syntactically it is compile-time contract programming, with `in` replaced by `if`, easier to describe that way. See that PR thread for more discussion.
Ah cool, that moves the discussion there. Thanks!
Jan 13 2019