www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Template constraints

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
There's been recurring discussion about failing constraints not 
generating nice error messages.

void fun(T)(T x) if (complicated_condition) { ... }
struct Type(T)(T x) if (complicated_condition) { ... }

If complicated_condition is not met, the symbol simply disappears and 
the compiler error message just lists is as a possible, but not viable, 
candidate.

I think one simple step toward improving things is pushing the condition 
in a static_assert inside type definitions:

void fun(T)(T x) if (complicated_condition) { ... } // no change
struct Type(T)(T x)
{
   static assert(complicated_condition, "Informative message.");
   ...
}

This should improve error messages for types (only). The rationale is 
that it's okay for types to refuse compilation because types, unlike 
functions, don't overload. The major reason for template constraints in 
functions is allowing for good overloading.


Andrei
Feb 14 2015
next sibling parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei 
Alexandrescu wrote:
 There's been recurring discussion about failing constraints not 
 generating nice error messages.

 void fun(T)(T x) if (complicated_condition) { ... }
 struct Type(T)(T x) if (complicated_condition) { ... }

 If complicated_condition is not met, the symbol simply 
 disappears and the compiler error message just lists is as a 
 possible, but not viable, candidate.

 I think one simple step toward improving things is pushing the 
 condition in a static_assert inside type definitions:

 void fun(T)(T x) if (complicated_condition) { ... } // no change
 struct Type(T)(T x)
 {
   static assert(complicated_condition, "Informative message.");
   ...
 }

 This should improve error messages for types (only). The 
 rationale is that it's okay for types to refuse compilation 
 because types, unlike functions, don't overload. The major 
 reason for template constraints in functions is allowing for 
 good overloading.


 Andrei
I agree, but also for function should conditions that do not effect overloading go into static asserts. For example if I were to write an algorithm that works with forward ranges and can be optimized for random access ranges but needs assignable elements in any case: void foo(R)(R r) if(isForwardRange!R && !isRandomAccessRange!R) { static assert(hasAssignableElements!R, "informative error msg"); } void foo(R)(R r) if(isRandomAccessRange!R) { static assert(hasAssignableElements!R, "informative error msg"); }
Feb 14 2015
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/14/15 9:06 AM, Tobias Pankrath wrote:
 I agree, but also for function should conditions that do not effect
 overloading go into static asserts.

 For example if I were to write an algorithm that works with forward
 ranges and can be optimized for random access ranges but needs
 assignable elements in any case:

 void foo(R)(R r) if(isForwardRange!R && !isRandomAccessRange!R)
 {
      static assert(hasAssignableElements!R, "informative error msg");
 }

 void foo(R)(R r) if(isRandomAccessRange!R)
 {
      static assert(hasAssignableElements!R, "informative error msg");
 }
That would impact cross-module overloading. -- Andrei
Feb 14 2015
prev sibling next sibling parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei 
Alexandrescu wrote:
 ...
 This should improve error messages for types (only). The 
 rationale is that it's okay for types to refuse compilation 
 because types, unlike functions, don't overload.
There was talk about enabling IFTI for constructors. If that is done, some types _will_ overload. But for the remaining ones (i.e. those that we don't want to overloadable), I think it's a good strategy.
Feb 14 2015
prev sibling next sibling parent "Xinok" <xinok live.com> writes:
On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei 
Alexandrescu wrote:
 There's been recurring discussion about failing constraints not 
 generating nice error messages.

 void fun(T)(T x) if (complicated_condition) { ... }
 struct Type(T)(T x) if (complicated_condition) { ... }

 If complicated_condition is not met, the symbol simply 
 disappears and the compiler error message just lists is as a 
 possible, but not viable, candidate.

 I think one simple step toward improving things is pushing the 
 condition in a static_assert inside type definitions:

 void fun(T)(T x) if (complicated_condition) { ... } // no change
 struct Type(T)(T x)
 {
   static assert(complicated_condition, "Informative message.");
   ...
 }

 This should improve error messages for types (only). The 
 rationale is that it's okay for types to refuse compilation 
 because types, unlike functions, don't overload. The major 
 reason for template constraints in functions is allowing for 
 good overloading.


 Andrei
I've done this myself for the same reason. I may split up the condition into multiple 'static assert' statements which gives a more specific error message. However, I wonder if there's a better solution that we can incorporate in to D. The trouble with template constraints is that, if you have complex conditions, there's no easy way to fall back to a default state. You would have to duplicate all the conditions and write "not this and not this and not this and ...". Template specializations can fall back to a default state, but not template constraints. So what if we were to add a feature that would allow us to do just that? void test(Range)(Range r) if(isRandomAccessRange!Range) { ... } void test(Range)(Range r) default { static assert(false, "descriptive error message"); } I'm not claiming this is a better solution; I'm simply putting the idea out there.
Feb 14 2015
prev sibling next sibling parent "Atila Neves" <atila.neves gmail.com> writes:
Even then this "hides" some errors and debugging isn't easy 
(figuring out why the template constraint failed). I've been 
planning on creating a DIP addressing this for ages, I should 
probably get around to that.

Atila

On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei 
Alexandrescu wrote:
 There's been recurring discussion about failing constraints not 
 generating nice error messages.

 void fun(T)(T x) if (complicated_condition) { ... }
 struct Type(T)(T x) if (complicated_condition) { ... }

 If complicated_condition is not met, the symbol simply 
 disappears and the compiler error message just lists is as a 
 possible, but not viable, candidate.

 I think one simple step toward improving things is pushing the 
 condition in a static_assert inside type definitions:

 void fun(T)(T x) if (complicated_condition) { ... } // no change
 struct Type(T)(T x)
 {
   static assert(complicated_condition, "Informative message.");
   ...
 }

 This should improve error messages for types (only). The 
 rationale is that it's okay for types to refuse compilation 
 because types, unlike functions, don't overload. The major 
 reason for template constraints in functions is allowing for 
 good overloading.


 Andrei
Feb 15 2015
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/14/15 12:00 PM, Andrei Alexandrescu wrote:
 There's been recurring discussion about failing constraints not
 generating nice error messages.

 void fun(T)(T x) if (complicated_condition) { ... }
 struct Type(T)(T x) if (complicated_condition) { ... }

 If complicated_condition is not met, the symbol simply disappears and
 the compiler error message just lists is as a possible, but not viable,
 candidate.

 I think one simple step toward improving things is pushing the condition
 in a static_assert inside type definitions:

 void fun(T)(T x) if (complicated_condition) { ... } // no change
 struct Type(T)(T x)
 {
    static assert(complicated_condition, "Informative message.");
    ...
 }

 This should improve error messages for types (only). The rationale is
 that it's okay for types to refuse compilation because types, unlike
 functions, don't overload. The major reason for template constraints in
 functions is allowing for good overloading.


 Andrei
Wait, isn't this OK? struct S(T) if(is(T == int)) { ... } struct S(T) if(is(T == double)) { ... } I mean we could do it like this: struct S(T) { static if(is(T == int)) { ... // int mode } else static if(is(T == double)) { ... // double mode } else static assert(0); } but that defeats the purpose of being able to split the "int mode" stuff from the "double mode" stuff. They may even be in multiple modules. Is this not "overloading" of types? I don't think this should be frowned upon. I think instead of this, we should try and just make the messages better. Note that something like you are suggesting requires much rewriting of code. Have you considered something like this? forum.dlang.org/post/m4nnrk$1ml5$1 digitalmars.com -Steve
Feb 16 2015
parent Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On 16/02/2015 19:31, Steven Schveighoffer wrote:
 Wait, isn't this OK?

 struct S(T) if(is(T == int))
 {
    ...
 }

 struct S(T) if(is(T == double))
 {
    ...
 }
...
 They may even be in multiple modules.

 Is this not "overloading" of types? I don't think this should be frowned
 upon.
OK, so the idea might not be appropriate for types. What about for methods though - if a method doesn't have overloads, static assert might be better than a constraint. Even where there are overloads, (part of) the constraint may be OK to push into static assert instead for better error messages.
 I think instead of this, we should try and just make the messages
 better. Note that something like you are suggesting requires much
 rewriting of code.
True, but until the compiler improves perhaps we could use this technique for methods e.g. in new code.
Feb 19 2015
prev sibling parent "Chris Williams" <yoreanon-chrisw yahoo.co.jp> writes:
On Saturday, 14 February 2015 at 17:00:33 UTC, Andrei 
Alexandrescu wrote:
 There's been recurring discussion about failing constraints not 
 generating nice error messages.

 void fun(T)(T x) if (complicated_condition) { ... }
 struct Type(T)(T x) if (complicated_condition) { ... }

 If complicated_condition is not met, the symbol simply 
 disappears and the compiler error message just lists is as a 
 possible, but not viable, candidate.

 I think one simple step toward improving things is pushing the 
 condition in a static_assert inside type definitions:

 void fun(T)(T x) if (complicated_condition) { ... } // no change
 struct Type(T)(T x)
 {
   static assert(complicated_condition, "Informative message.");
   ...
 }

 This should improve error messages for types (only). The 
 rationale is that it's okay for types to refuse compilation 
 because types, unlike functions, don't overload. The major 
 reason for template constraints in functions is allowing for 
 good overloading.


 Andrei
This seems like a lot of manual labor to cover a compiler limitation that is going to affect not just Phobos, but every D project which uses constraints (or template type specialization?) instead of interfaces. Ideally, the compiler would be able to say, "Well the function name and the parameter list exactly match these possibilities, so let's run through each constraint applied in the set of possibilities and explicitly dump the boolean result for each constraint."
Feb 17 2015