www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Fallback 'catch-all' template functions

reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
So, consider a set of overloads:

  void f(T)(T t) if(isSomething!T) {}
  void f(T)(T t) if(isSomethingElse!T) {}
  void f(T)(T t) {}

I have a recurring problem where I need a fallback function like the
bottom one, which should be used in lieu of a more precise match.
This is obviously an ambiguous call, but this is a pattern that comes
up an awful lot. How to do it in D?

I've asked this before, and people say:

  void f(T)(T t) if(!isSomething!T && !isSomethingElse!T) {}

Consider that more overloads are being introduced by users spread out
across many modules that define their own kind of T; this solution is
no good.
Aug 31 2016
next sibling parent Ethan Watson <gooberman gmail.com> writes:
On Thursday, 1 September 2016 at 05:37:50 UTC, Manu wrote:
 I have a recurring problem where I need a fallback function 
 like the bottom one, which should be used in lieu of a more 
 precise match.
+1. I've already hit this a few times with Binderoo. I would have assumed that constraints are just another form of specialisation, but it requires me to be explicit with the base functionality.
Sep 01 2016
prev sibling next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 1 September 2016 at 05:37:50 UTC, Manu wrote:
 So, consider a set of overloads:

   void f(T)(T t) if(isSomething!T) {}
   void f(T)(T t) if(isSomethingElse!T) {}
   void f(T)(T t) {}

 I have a recurring problem where I need a fallback function 
 like the bottom one, which should be used in lieu of a more 
 precise match. This is obviously an ambiguous call, but this is 
 a pattern that comes up an awful lot. How to do it in D?

 I've asked this before, and people say:

   void f(T)(T t) if(!isSomething!T && !isSomethingElse!T) {}

 Consider that more overloads are being introduced by users 
 spread out across many modules that define their own kind of T; 
 this solution is no good.
To my knowledge there is currently no clean way of doing this. The easiest workaround would be to introduce another name for the implementation. then it would look like void f(T)(T t) { static if (is(fImpl(t) == void)) { f(t); } else { // default impl here } }
Sep 01 2016
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 1 September 2016 at 08:44:38 UTC, Stefan Koch wrote:
 void f(T)(T t) {
   static if (is(fImpl(t) == void)) {
     f(t);
   } else {
     // default impl here
   }
 }
Was meant to be void f(T)(T t) { static if (is(fImpl(t) == void)) { fImpl(t); } else { // default impl here } }
Sep 01 2016
parent Andrea Fontana <nospam example.com> writes:
On Thursday, 1 September 2016 at 08:46:07 UTC, Stefan Koch wrote:
 On Thursday, 1 September 2016 at 08:44:38 UTC, Stefan Koch 
 wrote:
 void f(T)(T t) {
   static if (is(fImpl(t) == void)) {
     f(t);
   } else {
     // default impl here
   }
 }
Was meant to be void f(T)(T t) { static if (is(fImpl(t) == void)) { fImpl(t); } else { // default impl here } }
why not: static if (__traits(compiles, fImpl(t))) ? Andrea
Sep 01 2016
prev sibling parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 1 September 2016 at 18:44, Stefan Koch via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Thursday, 1 September 2016 at 05:37:50 UTC, Manu wrote:
 So, consider a set of overloads:

   void f(T)(T t) if(isSomething!T) {}
   void f(T)(T t) if(isSomethingElse!T) {}
   void f(T)(T t) {}

 I have a recurring problem where I need a fallback function like the
 bottom one, which should be used in lieu of a more precise match. This is
 obviously an ambiguous call, but this is a pattern that comes up an awful
 lot. How to do it in D?

 I've asked this before, and people say:

   void f(T)(T t) if(!isSomething!T && !isSomethingElse!T) {}

 Consider that more overloads are being introduced by users spread out
 across many modules that define their own kind of T; this solution is no
 good.
To my knowledge there is currently no clean way of doing this. The easiest workaround would be to introduce another name for the implementation. then it would look like void f(T)(T t) { static if (is(fImpl(t) == void)) { f(t); } else { // default impl here } }
This was my fallback plan, but it seems a bit shit.
Sep 01 2016
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 1 September 2016 at 09:08:31 UTC, Manu wrote:
 This was my fallback plan, but it seems a bit shit.
Hmm I get your point. But there is not really another way within the current langauge. Also overloading lot of templates with templates constraints will eat into your compile-time!
Sep 01 2016
parent Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 1 September 2016 at 19:16, Stefan Koch via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Thursday, 1 September 2016 at 09:08:31 UTC, Manu wrote:
 This was my fallback plan, but it seems a bit shit.
Hmm I get your point. But there is not really another way within the current langauge. Also overloading lot of templates with templates constraints will eat into your compile-time!
I know, but it's core language feature of D! Basically every function in the std library has some constraints. It's critical for anything range based... Perhaps if concepts existed, it might be a more direct and less computationally intensive mechanism to deal with it. I've been feeling like that'd really simplify the situation a lot for quite a few years now.
Sep 01 2016
prev sibling next sibling parent reply Dominikus Dittes Scherkl <Dominikus.Scherkl continental-corporation.com> writes:
On Thursday, 1 September 2016 at 05:37:50 UTC, Manu wrote:
 So, consider a set of overloads:

   void f(T)(T t) if(isSomething!T) {}
   void f(T)(T t) if(isSomethingElse!T) {}
   void f(T)(T t) {}

 I have a recurring problem where I need a fallback function 
 like the bottom one, which should be used in lieu of a more 
 precise match. This is obviously an ambiguous call, but this is 
 a pattern that comes up an awful lot. How to do it in D?

 I've asked this before, and people say:

   void f(T)(T t) if(!isSomething!T && !isSomethingElse!T) {}

 Consider that more overloads are being introduced by users 
 spread out across many modules that define their own kind of T; 
 this solution is no good.
Simply void f(T)(T t) { static if(isSomething!T) { } else static if(isSomethingElse!T) { } else { } } I personally hate overloads, especially if the condition has a fallback, so I like to see no condition in the function signature, what makes for a much cleaner API. I have never seen what benefit could be gained from having overloads. I think they are a relict from languages without static if.
Sep 01 2016
next sibling parent reply Dominikus Dittes Scherkl <Dominikus.Scherkl continental-corporation.com> writes:
On Thursday, 1 September 2016 at 10:43:50 UTC, Dominikus Dittes 
Scherkl wrote:
 I have never seen what benefit could be gained from having 
 overloads. I think they are a relict from languages without 
 static if.
I mean, overloads with same function signature except for the condition. Of course if the overloads have different parameters or return type, they may make sense. But they still uglyfy the API, so I try to avoid them - instead I use default parameters and template parameters what pretty much always works.
Sep 01 2016
parent Cauterite <cauterite gmail.com> writes:
On Thursday, 1 September 2016 at 10:50:18 UTC, Dominikus Dittes 
Scherkl wrote:
 On Thursday, 1 September 2016 at 10:43:50 UTC, Dominikus Dittes 
 Scherkl wrote:
 I have never seen what benefit could be gained from having 
 overloads. I think they are a relict from languages without 
 static if.
I mean, overloads with same function signature except for the condition. Of course if the overloads have different parameters or return type, they may make sense. But they still uglyfy the API, so I try to avoid them - instead I use default parameters and template parameters what pretty much always works.
When you're specialising on type classes rather than concrete types, you have no choice: auto f(T)(T source) if (is(ElementType!T : int)) {...}; auto f(T)(T source) if (is(ElementType!T : Object)) {...}; There is nothing you can write in the template parameters list to enable the same kind of specialisation.
Sep 01 2016
prev sibling next sibling parent reply Ethan Watson <gooberman gmail.com> writes:
On Thursday, 1 September 2016 at 10:43:50 UTC, Dominikus Dittes 
Scherkl wrote:
 I have never seen what benefit could be gained from having 
 overloads.
Oh, it's perfectly fine if you're not writing a library that's designed to allow user extension by going the "all in one" method. If you encourage your users to modify your function itself, they can no longer drop in a new version and have to do a merge. Templates in general seem weak for libraries in D, which is a pain considering templates are one of the areas where the language otherwise excels.
Sep 01 2016
parent reply Dominikus Dittes Scherkl <Dominikus.Scherkl continental-corporation.com> writes:
On Thursday, 1 September 2016 at 10:53:01 UTC, Ethan Watson wrote:
 On Thursday, 1 September 2016 at 10:43:50 UTC, Dominikus Dittes 
 Scherkl wrote:
 I have never seen what benefit could be gained from having 
 overloads.
Oh, it's perfectly fine if you're not writing a library that's designed to allow user extension by going the "all in one" method. If you encourage your users to modify your function itself, they can no longer drop in a new version and have to do a merge.
Ok, that may be fine, until you reach the point with the fallback version: if after that point someone "drops in" a new version, he silently changes the behavior of the function, because he "steals" some type which used to use the fallback version. --> overloads make only sense, if a function is NOT for all types, so you can add an overload for some type that was not considered so far, but you should not change the behaviour for some type that was already covered. At all, most of the time I prefer that the users directly change the function, yes.
Sep 01 2016
parent Ethan Watson <gooberman gmail.com> writes:
On Thursday, 1 September 2016 at 11:01:28 UTC, Dominikus Dittes 
Scherkl wrote:
 Ok, that may be fine, until you reach the point with the 
 fallback version: if after that point someone "drops in" a new 
 version, he silently changes the behavior of the function, 
 because he "steals" some type which used to use the fallback 
 version.
I don't see how that can be considered anything other than "expected behaviour" and thus ensure your design takes this in to account. If you give your user the keys to the kingdom, you need to expect them to use it.
Sep 01 2016
prev sibling parent Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 1 September 2016 at 20:43, Dominikus Dittes Scherkl via
Digitalmars-d <digitalmars-d puremagic.com> wrote:
 On Thursday, 1 September 2016 at 05:37:50 UTC, Manu wrote:
 Consider that more overloads are being introduced by users spread out
 across many modules that define their own kind of T; this solution is no
 good.
Simply void f(T)(T t) { static if(isSomething!T) { } else static if(isSomethingElse!T) { } else { } } I personally hate overloads, especially if the condition has a fallback, so I like to see no condition in the function signature, what makes for a much cleaner API. I have never seen what benefit could be gained from having overloads. I think they are a relict from languages without static if.
I think you must have not read the line of text that you replied to...
Sep 01 2016
prev sibling next sibling parent Enamex <enamex+d outlook.com> writes:
On Thursday, 1 September 2016 at 05:37:50 UTC, Manu wrote:
 So, consider a set of overloads:

   void f(T)(T t) if(isSomething!T) {}
   void f(T)(T t) if(isSomethingElse!T) {}
   void f(T)(T t) {}

 I have a recurring problem where I need a fallback function 
 like the bottom one, which should be used in lieu of a more 
 precise match. This is obviously an ambiguous call, but this is 
 a pattern that comes up an awful lot. How to do it in D?

 I've asked this before, and people say:

   void f(T)(T t) if(!isSomething!T && !isSomethingElse!T) {}

 Consider that more overloads are being introduced by users 
 spread out across many modules that define their own kind of T; 
 this solution is no good.
An easy workaround with a bit of boilerplate would be: void f(T)(T t) { static if(isSomething!T) { // special impl } else { fallback_f(t); } } void fallback_f(T)(T t) { ... }
Sep 01 2016
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/1/16 1:37 AM, Manu via Digitalmars-d wrote:
 So, consider a set of overloads:

   void f(T)(T t) if(isSomething!T) {}
   void f(T)(T t) if(isSomethingElse!T) {}
   void f(T)(T t) {}

 I have a recurring problem where I need a fallback function like the
 bottom one, which should be used in lieu of a more precise match.
 This is obviously an ambiguous call, but this is a pattern that comes
 up an awful lot. How to do it in D?

 I've asked this before, and people say:

   void f(T)(T t) if(!isSomething!T && !isSomethingElse!T) {}

 Consider that more overloads are being introduced by users spread out
 across many modules that define their own kind of T; this solution is
 no good.
I agree. Note that if(isSomethingElse!T) may also need to have if(!isSomething!T && isSomethingElse!T). A suggestion in the past was to allow "else" clauses with if constraints. I had envisioned: void f(T)(T t) if(isSomething!T) {} void f(T)(T t) else if(isSomethingElse!T) {} void f(T)(T t) else {} But someone also suggested this more DRY solution as well (can't remember the thread for it): void f(T)(T t) if(isSomething!T) {} else if(isSomeghingElse!T) {} else {} Obviously this doesn't work across modules, but how does that even work? You need some sort of ordering for an if/else if/else scheme to work. Having a "fallback" template could potentially define a way to handle the default, but it doesn't fix the other issues. I don't know if it's because of the current rules, or just natural, but typically I'm not splitting my template functions between many modules. -Steve
Sep 01 2016
parent reply Meta <jared771 gmail.com> writes:
On Thursday, 1 September 2016 at 16:50:49 UTC, Steven 
Schveighoffer wrote:
 I agree. Note that if(isSomethingElse!T) may also need to have 
 if(!isSomething!T && isSomethingElse!T).

 A suggestion in the past was to allow "else" clauses with if 
 constraints. I had envisioned:

 void f(T)(T t) if(isSomething!T) {}
 void f(T)(T t) else if(isSomethingElse!T) {}
 void f(T)(T t) else {}

 But someone also suggested this more DRY solution as well 
 (can't remember the thread for it):

 void f(T)(T t) if(isSomething!T) {}
           else if(isSomeghingElse!T) {}
           else {}

 Obviously this doesn't work across modules, but how does that 
 even work? You need some sort of ordering for an if/else 
 if/else scheme to work.

 Having a "fallback" template could potentially define a way to 
 handle the default, but it doesn't fix the other issues.

 I don't know if it's because of the current rules, or just 
 natural, but typically I'm not splitting my template functions 
 between many modules.

 -Steve
I just thought of this, but cannot test if it works. If it does, maybe it would be a suitable solution? void f(T)(T t) if(isSomething!T) {} void f(T)(T t) if(isSomethingElse!T) {} //Taken if no other "overload" of f will intantiate with the given T void f(T)(T t) if(!__traits(compiles, alias _ = .f!T)) {}
Sep 01 2016
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01.09.2016 19:21, Meta wrote:
 ...

 I just thought of this, but cannot test if it works. If it does, maybe
 it would be a suitable solution?

 void f(T)(T t) if(isSomething!T) {}
 void f(T)(T t) if(isSomethingElse!T) {}
 //Taken if no other "overload" of f will intantiate with the given T
 void f(T)(T t) if(!__traits(compiles, alias _ = .f!T)) {}
It shouldn't work, but DMD currently seems to allow it. (If you fix the syntax error.) I would expect it to break later. The following causes an ICE (DMD segfaults). import std.stdio; int f(T)(T t) if(!__traits(compiles,.f!T)) { return 0; } int f(T)(T t) if(!__traits(compiles,.f!T)) { return 1; } void main(){ writeln(f(2)); }
Sep 01 2016
next sibling parent reply Meta <jared771 gmail.com> writes:
On Thursday, 1 September 2016 at 17:49:13 UTC, Timon Gehr wrote:
 On 01.09.2016 19:21, Meta wrote:
 ...

 I just thought of this, but cannot test if it works. If it 
 does, maybe
 it would be a suitable solution?

 void f(T)(T t) if(isSomething!T) {}
 void f(T)(T t) if(isSomethingElse!T) {}
 //Taken if no other "overload" of f will intantiate with the 
 given T
 void f(T)(T t) if(!__traits(compiles, alias _ = .f!T)) {}
It shouldn't work, but DMD currently seems to allow it. (If you fix the syntax error.) I would expect it to break later. The following causes an ICE (DMD segfaults). import std.stdio; int f(T)(T t) if(!__traits(compiles,.f!T)) { return 0; } int f(T)(T t) if(!__traits(compiles,.f!T)) { return 1; } void main(){ writeln(f(2)); }
The idea is that there'd only be one such "fallback" template, so that you cannot get into a situation such as this. I'm guessing the ICE is due to a recursive dependency between the two f templates?
Sep 01 2016
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01.09.2016 19:55, Meta wrote:
 On Thursday, 1 September 2016 at 17:49:13 UTC, Timon Gehr wrote:
 On 01.09.2016 19:21, Meta wrote:
 ...

 I just thought of this, but cannot test if it works. If it does, maybe
 it would be a suitable solution?

 void f(T)(T t) if(isSomething!T) {}
 void f(T)(T t) if(isSomethingElse!T) {}
 //Taken if no other "overload" of f will intantiate with the given T
 void f(T)(T t) if(!__traits(compiles, alias _ = .f!T)) {}
It shouldn't work, but DMD currently seems to allow it. (If you fix the syntax error.) I would expect it to break later. The following causes an ICE (DMD segfaults). import std.stdio; int f(T)(T t) if(!__traits(compiles,.f!T)) { return 0; } int f(T)(T t) if(!__traits(compiles,.f!T)) { return 1; } void main(){ writeln(f(2)); }
The idea is that there'd only be one such "fallback" template, so that you cannot get into a situation such as this. I'm guessing the ICE is due to a recursive dependency between the two f templates?
I posted the ICE to show that DMD does not necessarily have a clear concept of how your code should be interpreted. Note that you are essentially saying: "If not X then X". It's very easy to run into behaviour that seems inconsistent once you do things like this. enum isSomething(T)=false; int f(T)(T t) if(isSomething!T){ return 0; } int f(T)(T t) if(!compiles!".f!int") { return 2; } enum compiles(string s) = __traits(compiles,mixin(s)); pragma(msg, compiles!".f!int"); // false pragma(msg, __traits(compiles,.f!int)); // true DMD cannot properly process code like this (i.e. code that contradicts itself, where the same expression in the same context can be true in one part of the compilation, but false later). Examples can be constructed where the semantics of the resulting code depends on the order that modules are passed on the command line. It's not specified anywhere what should happen, and it is not immediately clear. DMD shouldn't accept code like this in the first place. It's very brittle, the result depends on random compiler implementation details.
Sep 01 2016
parent reply Meta <jared771 gmail.com> writes:
On Thursday, 1 September 2016 at 18:24:13 UTC, Timon Gehr wrote:
 The idea is that there'd only be one such "fallback" template, 
 so that
 you cannot get into a situation such as this. I'm guessing the 
 ICE is
 due to a recursive dependency between the two f templates?
I posted the ICE to show that DMD does not necessarily have a clear concept of how your code should be interpreted. Note that you are essentially saying: "If not X then X". It's very easy to run into behaviour that seems inconsistent once you do things like this.
Well, I'd argue that's not quite right and the correct interpretation is "If not the other X then this X", due to the `!__traits(compiles, .f!T)`, explicitly telling the compiler to check if the *other* "overloads" compile. I don't actually know whether template constraints are considered to be at module scope or in the template scope, though, so maybe I'm completely wrong on this.
 enum isSomething(T)=false;

 int f(T)(T t) if(isSomething!T){
     return 0;
 }
 int f(T)(T t) if(!compiles!".f!int") {
     return 2;
 }

 enum compiles(string s) = __traits(compiles,mixin(s));
 pragma(msg, compiles!".f!int");         // false
 pragma(msg, __traits(compiles,.f!int)); // true


 DMD cannot properly process code like this (i.e. code that 
 contradicts itself, where the same expression in the same 
 context can be true in one part of the compilation, but false 
 later). Examples can be constructed where the semantics of the 
 resulting code depends on the order that modules are passed on 
 the command line. It's not specified anywhere what should 
 happen, and it is not immediately clear.

 DMD shouldn't accept code like this in the first place. It's 
 very brittle, the result depends on random compiler 
 implementation details.
I would argue that this is an entirely different case than my example, but we are getting into compiler implementation details that I know nothing about, so I can't actually say whether it is (or should) be valid code.
Sep 01 2016
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01.09.2016 21:02, Meta wrote:
 On Thursday, 1 September 2016 at 18:24:13 UTC, Timon Gehr wrote:
 The idea is that there'd only be one such "fallback" template, so that
 you cannot get into a situation such as this. I'm guessing the ICE is
 due to a recursive dependency between the two f templates?
I posted the ICE to show that DMD does not necessarily have a clear concept of how your code should be interpreted. Note that you are essentially saying: "If not X then X". It's very easy to run into behaviour that seems inconsistent once you do things like this.
Well, I'd argue that's not quite right and the correct interpretation is "If not the other X then this X", due to the `!__traits(compiles, .f!T)`, explicitly telling the compiler to check if the *other* "overloads" compile.
Even if that was the intention of the compiler implementation, the example below demonstrates why it cannot work.
 I don't actually know whether template constraints
 are considered to be at module scope or in the template scope, though,
 so maybe I'm completely wrong on this.
 ...
Template scope, but /nothing/ about '.' says "other".
 enum isSomething(T)=false;

 int f(T)(T t) if(isSomething!T){
     return 0;
 }
 int f(T)(T t) if(!compiles!".f!int") {
     return 2;
 }

 enum compiles(string s) = __traits(compiles,mixin(s));
 pragma(msg, compiles!".f!int");         // false
 pragma(msg, __traits(compiles,.f!int)); // true


 DMD cannot properly process code like this (i.e. code that contradicts
 itself, where the same expression in the same context can be true in
 one part of the compilation, but false later). Examples can be
 constructed where the semantics of the resulting code depends on the
 order that modules are passed on the command line. It's not specified
 anywhere what should happen, and it is not immediately clear.

 DMD shouldn't accept code like this in the first place. It's very
 brittle, the result depends on random compiler implementation details.
I would argue that this is an entirely different case than my example, but we are getting into compiler implementation details that I know nothing about, so I can't actually say whether it is (or should) be valid code.
It's basically the same thing.
Sep 01 2016
parent Meta <jared771 gmail.com> writes:
On Thursday, 1 September 2016 at 19:32:23 UTC, Timon Gehr wrote:
 Well, I'd argue that's not quite right and the correct 
 interpretation is
 "If not the other X then this X", due to the 
 `!__traits(compiles,
 .f!T)`, explicitly telling the compiler to check if the *other*
 "overloads" compile.
Even if that was the intention of the compiler implementation, the example below demonstrates why it cannot work.
 I don't actually know whether template constraints
 are considered to be at module scope or in the template scope, 
 though,
 so maybe I'm completely wrong on this.
 ...
Template scope, but /nothing/ about '.' says "other".
It means to look up the symbol f at module scope, so I guess it depends on whether the compiler excludes the current template from that lookup. template f() if (someCondition) {} template f() if (someOtherCondition) {} template f() if (!__traits(compiles, { alias _ = .f!(); })) {} Does `.f` refer to all symbols named f at module scope, or all symbols named f *other than* the symbol for which the current template constraint is being processed? Actually, I just convinced myself that it's obviously not the latter and so must be the former, and now I see why this shouldn't work.
Sep 01 2016
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/1/2016 10:49 AM, Timon Gehr wrote:
 The following causes an ICE (DMD segfaults).

 import std.stdio;

 int f(T)(T t) if(!__traits(compiles,.f!T)) {
     return 0;
 }
 int f(T)(T t) if(!__traits(compiles,.f!T)) {
     return 1;
 }

 void main(){
     writeln(f(2));
 }
Please post seg faults to bugzilla.
Sep 02 2016
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03.09.2016 05:15, Walter Bright wrote:
 On 9/1/2016 10:49 AM, Timon Gehr wrote:
 The following causes an ICE (DMD segfaults).

 import std.stdio;

 int f(T)(T t) if(!__traits(compiles,.f!T)) {
     return 0;
 }
 int f(T)(T t) if(!__traits(compiles,.f!T)) {
     return 1;
 }

 void main(){
     writeln(f(2));
 }
Please post seg faults to bugzilla.
It had already been reported: https://issues.dlang.org/show_bug.cgi?id=11856 I have added this example to the report.
Sep 03 2016
parent Walter Bright <newshound2 digitalmars.com> writes:
On 9/3/2016 2:19 AM, Timon Gehr wrote:
 It had already been reported:
 https://issues.dlang.org/show_bug.cgi?id=11856

 I have added this example to the report.
Thank you!
Sep 03 2016
prev sibling parent Cauterite <cauterite gmail.com> writes:
On Thursday, 1 September 2016 at 17:21:02 UTC, Meta wrote:
 I just thought of this, but cannot test if it works. If it 
 does, maybe it would be a suitable solution?

 void f(T)(T t) if(isSomething!T) {}
 void f(T)(T t) if(isSomethingElse!T) {}
 //Taken if no other "overload" of f will intantiate with the 
 given T
 void f(T)(T t) if(!__traits(compiles, alias _ = .f!T)) {}
That's so dirty, I love it. I imagine if __traits(getOverloads worked for templates we could actually use this pretty reliably (by excluding the fallback template from the search).
Sep 01 2016
prev sibling next sibling parent reply Xinok <xinok live.com> writes:
On Thursday, 1 September 2016 at 05:37:50 UTC, Manu wrote:
 So, consider a set of overloads:

   void f(T)(T t) if(isSomething!T) {}
   void f(T)(T t) if(isSomethingElse!T) {}
   void f(T)(T t) {}

 I have a recurring problem where I need a fallback function 
 like the bottom one, which should be used in lieu of a more 
 precise match. This is obviously an ambiguous call, but this is 
 a pattern that comes up an awful lot. How to do it in D?

 I've asked this before, and people say:

   void f(T)(T t) if(!isSomething!T && !isSomethingElse!T) {}

 Consider that more overloads are being introduced by users 
 spread out across many modules that define their own kind of T; 
 this solution is no good.
In the past, I have suggested using the "default" keyword to specify a fallback function of this kind. I think it's a useful pattern for generic algorithms that have optimized variants on specific types for performance. void f(T)(T t) if(isSomething!T) {} void f(T)(T t) if(isSomethingElse!T) {} void f(T)(T t) default {}
Sep 02 2016
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 3 September 2016 at 00:18, Xinok via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 In the past, I have suggested using the "default" keyword to specify a
 fallback function of this kind. I think it's a useful pattern for generic
 algorithms that have optimized variants on specific types for performance.

    void f(T)(T t) if(isSomething!T) {}
    void f(T)(T t) if(isSomethingElse!T) {}
    void f(T)(T t) default {}
It's an interesting idea... flesh out a DIP?
Sep 02 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:
 On 3 September 2016 at 00:18, Xinok via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 In the past, I have suggested using the "default" keyword to specify a
 fallback function of this kind. I think it's a useful pattern for generic
 algorithms that have optimized variants on specific types for performance.

    void f(T)(T t) if(isSomething!T) {}
    void f(T)(T t) if(isSomethingElse!T) {}
    void f(T)(T t) default {}
It's an interesting idea... flesh out a DIP?
We're better off without that. -- Andrei
Sep 02 2016
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 3 September 2016 at 11:38, Andrei Alexandrescu via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:
 On 3 September 2016 at 00:18, Xinok via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 In the past, I have suggested using the "default" keyword to specify a
 fallback function of this kind. I think it's a useful pattern for generic
 algorithms that have optimized variants on specific types for
 performance.

    void f(T)(T t) if(isSomething!T) {}
    void f(T)(T t) if(isSomethingElse!T) {}
    void f(T)(T t) default {}
It's an interesting idea... flesh out a DIP?
We're better off without that. -- Andrei
Then we need a decent way to do this. As I've just expressed in the ADL thread, this whole pattern, which is alleged as one of D's core offerings; templates + UFCS -> pipeline programming (or 'component' programming as Walter likes to call it), is loaded with issues, and other than SFINAE being a pita, and UFCS coming to C++ 'soon'™, the whole thing is much easier in C++ right now.
Sep 02 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/3/16 7:29 AM, Manu via Digitalmars-d wrote:
 On 3 September 2016 at 11:38, Andrei Alexandrescu via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:
 On 3 September 2016 at 00:18, Xinok via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 In the past, I have suggested using the "default" keyword to specify a
 fallback function of this kind. I think it's a useful pattern for generic
 algorithms that have optimized variants on specific types for
 performance.

    void f(T)(T t) if(isSomething!T) {}
    void f(T)(T t) if(isSomethingElse!T) {}
    void f(T)(T t) default {}
It's an interesting idea... flesh out a DIP?
We're better off without that. -- Andrei
Then we need a decent way to do this.
Use static if inside the function. The entire notion of "call this function if you can't find something somewhere that works" is questionable. -- Andrei
Sep 03 2016
next sibling parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 3 September 2016 at 22:24, Andrei Alexandrescu via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 9/3/16 7:29 AM, Manu via Digitalmars-d wrote:
 On 3 September 2016 at 11:38, Andrei Alexandrescu via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:
 On 3 September 2016 at 00:18, Xinok via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 In the past, I have suggested using the "default" keyword to specify a
 fallback function of this kind. I think it's a useful pattern for
 generic
 algorithms that have optimized variants on specific types for
 performance.

    void f(T)(T t) if(isSomething!T) {}
    void f(T)(T t) if(isSomethingElse!T) {}
    void f(T)(T t) default {}
It's an interesting idea... flesh out a DIP?
We're better off without that. -- Andrei
Then we need a decent way to do this.
Use static if inside the function. The entire notion of "call this function if you can't find something somewhere that works" is questionable. -- Andrei
It's all about: generic function 'lerp()' exists... user supplies new type, user extends standard named function 'lerp()' for their new type. We do this sort of things a lot. Consider std.conv.to?
Sep 03 2016
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/3/16 6:10 PM, Manu via Digitalmars-d wrote:
 We do this sort of things a lot. Consider std.conv.to?
std.conv.to is not a frequently done thing. -- Andrei
Sep 03 2016
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Sat, 3 Sep 2016 14:24:13 +0200
schrieb Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:

 On 9/3/16 7:29 AM, Manu via Digitalmars-d wrote:
 On 3 September 2016 at 11:38, Andrei Alexandrescu via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:  
 On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:  
 On 3 September 2016 at 00:18, Xinok via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:  
 In the past, I have suggested using the "default" keyword to specify a
 fallback function of this kind. I think it's a useful pattern for generic
 algorithms that have optimized variants on specific types for
 performance.

    void f(T)(T t) if(isSomething!T) {}
    void f(T)(T t) if(isSomethingElse!T) {}
    void f(T)(T t) default {}  
It's an interesting idea... flesh out a DIP?
We're better off without that. -- Andrei
Then we need a decent way to do this.
Use static if inside the function. The entire notion of "call this function if you can't find something somewhere that works" is questionable. -- Andrei
This notion is what drives template specializations in D: char foo(T)(T t) { return '?'; } // Call this if nothing else matches char foo(T : int)(T t) { return 'i'; } // Call this for ints char foo(T : string)(T t) { return 's'; } // Call this for strings Granted it's a bit more fuzzy and talks about "more specialized", but it is ultimately the same as a hard fallback when foo(3.4) is called. UFCS functions are also only invoked if there is no method with that name on the type, or any type reachable through "alias this" or opDot and there is no opDispatch on said types accepting that name. However questionable the notion is, it is common in D today. Static if also wont work in cases where the signature needs to be different, like overloads of opCast, where one returns bool and is const and another returns a different view on the same thing by ref and is inout. opCast(T:bool) would be unclean, as it also matches types T with a boolean 'alias this'. One can also imagine that some overloads want to take their arguments by value while others take it by ref. -- Marco
Sep 03 2016
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/31/2016 10:37 PM, Manu via Digitalmars-d wrote:
 So, consider a set of overloads:

   void f(T)(T t) if(isSomething!T) {}
   void f(T)(T t) if(isSomethingElse!T) {}
   void f(T)(T t) {}

 I have a recurring problem where I need a fallback function like the
 bottom one, which should be used in lieu of a more precise match.
 This is obviously an ambiguous call, but this is a pattern that comes
 up an awful lot. How to do it in D?
import core.stdc.stdio; void f(T:T)(T t) if(is(T == int)) { printf("case int\n"); } void f(T:T)(T t) if(is(T == uint)) { printf("case uint\n"); } void f(T)(T t) { printf("case default\n"); } void main() { f(1); f(1u); f(1.0); } ---- A bit odd, but far better than SFINAE.
Sep 03 2016
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 3 September 2016 at 19:19, Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 8/31/2016 10:37 PM, Manu via Digitalmars-d wrote:
 So, consider a set of overloads:

   void f(T)(T t) if(isSomething!T) {}
   void f(T)(T t) if(isSomethingElse!T) {}
   void f(T)(T t) {}

 I have a recurring problem where I need a fallback function like the
 bottom one, which should be used in lieu of a more precise match.
 This is obviously an ambiguous call, but this is a pattern that comes
 up an awful lot. How to do it in D?
import core.stdc.stdio; void f(T:T)(T t) if(is(T == int)) { printf("case int\n"); } void f(T:T)(T t) if(is(T == uint)) { printf("case uint\n"); } void f(T)(T t) { printf("case default\n"); } void main() { f(1); f(1u); f(1.0); } ---- A bit odd, but far better than SFINAE.
This is interesting. Can you explain how that works?
Sep 03 2016
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/3/2016 2:43 AM, Manu via Digitalmars-d wrote:
 This is interesting. Can you explain how that works?
Specializations are preferred over non-specializations, and T:T is the identity specialization.
Sep 03 2016
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2016-09-03 11:56, Walter Bright wrote:

 Specializations are preferred over non-specializations, and T:T is the
 identity specialization.
What if the compiler can prefer template constraint in the same way? Then perhaps this workaround could be avoided. -- /Jacob Carlborg
Sep 03 2016
parent Walter Bright <newshound2 digitalmars.com> writes:
On 9/3/2016 4:22 AM, Jacob Carlborg wrote:
 On 2016-09-03 11:56, Walter Bright wrote:

 Specializations are preferred over non-specializations, and T:T is the
 identity specialization.
What if the compiler can prefer template constraint in the same way? Then perhaps this workaround could be avoided.
It's extremely risky to invent new template lookup rules for existing code. Who knows whose carefully constructed template ox will get gored by it. The suggested technique is simple, works on all the existing D compilers since constraints, and doesn't break anything. I suggest we move on.
Sep 03 2016
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Sat, 3 Sep 2016 02:56:16 -0700
schrieb Walter Bright <newshound2 digitalmars.com>:

 On 9/3/2016 2:43 AM, Manu via Digitalmars-d wrote:
 This is interesting. Can you explain how that works?  
Specializations are preferred over non-specializations, and T:T is the identity specialization.
Pretty cool, I'll try that next time I write templated overload sets! -- Marco
Sep 03 2016