September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ethan Watson | 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.
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dominikus Dittes Scherkl | 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...
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dominikus Dittes Scherkl | 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.
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | 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) { ... }
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dominikus Dittes Scherkl | 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.
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stefan Koch | 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
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | 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
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | 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)) {}
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Meta | 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));
}
|
September 01, 2016 Re: Fallback 'catch-all' template functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | 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?
|
Copyright © 1999-2021 by the D Language Foundation