www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - One more simple idea for simplifying constraints

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Sometimes we use constraints to distinguish different approach to 
implementation, e.g. find for a string is different than find for 
forward ranges is different from find for input ranges etc.

One simple idea here (which Walter also advised a while ago) is to push 
implementation details inside the, well, implementation, e.g.:

Type functionName(TemplateArgs)(Args)
if (HighLevelConstraint)
{
   static if (LowLevelCapability1)
   {
     .. specialization 1 ...
   }
   else static if (LowLevelCapability1)
   {
     ... specialization 2 ...
   }
   else
   {
     .. conservative impl ...
   }
}

That way the user/documentation only deals with the high level 
constraint (the minimum necessary for the function to work). Inside, the 
function decides the exact approach.

It's an obvious idea now but many times in the past I didn't pursue it.


Andrei
Apr 25 2016
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 25 April 2016 at 18:50:28 UTC, Andrei Alexandrescu 
wrote:
 Sometimes we use constraints to distinguish different approach 
 to implementation, e.g. find for a string is different than 
 find for forward ranges is different from find for input ranges 
 etc.

 One simple idea here (which Walter also advised a while ago) is 
 to push implementation details inside the, well, 
 implementation, e.g.:

 Type functionName(TemplateArgs)(Args)
 if (HighLevelConstraint)
 {
   static if (LowLevelCapability1)
   {
     .. specialization 1 ...
   }
   else static if (LowLevelCapability1)
   {
     ... specialization 2 ...
   }
   else
   {
     .. conservative impl ...
   }
 }

 That way the user/documentation only deals with the high level 
 constraint (the minimum necessary for the function to work). 
 Inside, the function decides the exact approach.

 It's an obvious idea now but many times in the past I didn't 
 pursue it.


 Andrei
While working on implementing template constraints for SDC. I had a similar idea, basically a template constraint can be rewritten as a static if inside the template. R Tmpl (U) (U u) if (is(U == int)) { pragma(msg, "U is an int"); } R Tmpl (U) (U u) if (is(U == string)) { pragma(msg, "U is a string"); } is equivalent to R Templ (U u) { static if (is(U == int)) { pragma(msg, "U is an int"); } else static if (is(U == string)) { pragma(msg, "U is a string"); } else static assert(0, "Can only be instantiated with int or string"); } Except that you just get one error message instead of 2 overloads displayed.
Apr 25 2016
parent deadalnix <deadalnix gmail.com> writes:
On Tuesday, 26 April 2016 at 03:17:09 UTC, Stefan Koch wrote:
 On Monday, 25 April 2016 at 18:50:28 UTC, Andrei Alexandrescu 
 wrote:
 Sometimes we use constraints to distinguish different approach 
 to implementation, e.g. find for a string is different than 
 find for forward ranges is different from find for input 
 ranges etc.

 One simple idea here (which Walter also advised a while ago) 
 is to push implementation details inside the, well, 
 implementation, e.g.:

 Type functionName(TemplateArgs)(Args)
 if (HighLevelConstraint)
 {
   static if (LowLevelCapability1)
   {
     .. specialization 1 ...
   }
   else static if (LowLevelCapability1)
   {
     ... specialization 2 ...
   }
   else
   {
     .. conservative impl ...
   }
 }

 That way the user/documentation only deals with the high level 
 constraint (the minimum necessary for the function to work). 
 Inside, the function decides the exact approach.

 It's an obvious idea now but many times in the past I didn't 
 pursue it.


 Andrei
While working on implementing template constraints for SDC. I had a similar idea, basically a template constraint can be rewritten as a static if inside the template. R Tmpl (U) (U u) if (is(U == int)) { pragma(msg, "U is an int"); } R Tmpl (U) (U u) if (is(U == string)) { pragma(msg, "U is a string"); } is equivalent to R Templ (U u) { static if (is(U == int)) { pragma(msg, "U is an int"); } else static if (is(U == string)) { pragma(msg, "U is a string"); } else static assert(0, "Can only be instantiated with int or string"); } Except that you just get one error message instead of 2 overloads displayed.
It is not semantically equivalent.
Apr 25 2016
prev sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Mon, Apr 25, 2016 at 02:50:28PM -0400, Andrei Alexandrescu via Digitalmars-d
wrote:
 Sometimes we use constraints to distinguish different approach to
 implementation, e.g. find for a string is different than find for
 forward ranges is different from find for input ranges etc.
 
 One simple idea here (which Walter also advised a while ago) is to
 push implementation details inside the, well, implementation, e.g.:
 
 Type functionName(TemplateArgs)(Args)
 if (HighLevelConstraint)
 {
   static if (LowLevelCapability1)
   {
     .. specialization 1 ...
   }
   else static if (LowLevelCapability1)
   {
     ... specialization 2 ...
   }
   else
   {
     .. conservative impl ...
   }
 }
[...] This can also be written: template functionName(TemplateArgs) if (HighLevelConstraint) { static if (...) Type functionName(Args) { // specialization 1 } else static if (...) Type functionName(Args) { // specialization 2 } else Type functionName(Args) { // conservative impl } } This form might be preferable if there's some common code between different specializations that can be factored out of the static if (but inside the template body). Many of the overloads in Phobos arguably should be written in this way, because they aren't really overloads in the traditional sense of using the same logical name for multiple combinations of arguments, but are more like template specializations, i.e., same name with same logical combination of arguments, but possibly different argument types. E.g., find() with certain string types have specialized implementations, but from the user's POV find(string s) really should be considered a single overload, not multiple overloads differing (in terms of API) only in sig constraints. Consolidating specializations this way will not only allow better error messages, but will also make the code easier to maintain. I have advocated for this approach in the past, but the idea was basically shot down. Hopefully this time round people will see its merits. T -- Life is too short to run proprietary software. -- Bdale Garbee
Apr 25 2016