Re: Fallback 'catch-all' template functions

2016-09-03 Thread Marco Leise via Digitalmars-d
Am Sat, 3 Sep 2016 02:56:16 -0700
schrieb Walter Bright :

> 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



Re: Fallback 'catch-all' template functions

2016-09-03 Thread Marco Leise via Digitalmars-d
Am Sat, 3 Sep 2016 14:24:13 +0200
schrieb Andrei Alexandrescu :

> On 9/3/16 7:29 AM, Manu via Digitalmars-d wrote:
> > On 3 September 2016 at 11:38, Andrei Alexandrescu via Digitalmars-d
> >  wrote:  
> >> On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:  
> >>>
> >>> On 3 September 2016 at 00:18, Xinok via Digitalmars-d
> >>>  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



Re: Fallback 'catch-all' template functions

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

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


Re: Fallback 'catch-all' template functions

2016-09-03 Thread Manu via Digitalmars-d
On 3 September 2016 at 22:24, Andrei Alexandrescu via Digitalmars-d
 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
>>  wrote:
>>>
>>> On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:


 On 3 September 2016 at 00:18, Xinok via Digitalmars-d
  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?


Re: Fallback 'catch-all' template functions

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 7:29 AM, Manu via Digitalmars-d wrote:

On 3 September 2016 at 11:38, Andrei Alexandrescu via Digitalmars-d
 wrote:

On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:


On 3 September 2016 at 00:18, Xinok via Digitalmars-d
 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





Re: Fallback 'catch-all' template functions

2016-09-03 Thread Walter Bright via Digitalmars-d

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.



Re: Fallback 'catch-all' template functions

2016-09-03 Thread Jacob Carlborg via Digitalmars-d

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


Re: Fallback 'catch-all' template functions

2016-09-03 Thread Walter Bright via Digitalmars-d

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.


Re: Fallback 'catch-all' template functions

2016-09-03 Thread Manu via Digitalmars-d
On 3 September 2016 at 19:19, Walter Bright via Digitalmars-d
 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?


Re: Fallback 'catch-all' template functions

2016-09-03 Thread Walter Bright via Digitalmars-d

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!


Re: Fallback 'catch-all' template functions

2016-09-03 Thread Timon Gehr via Digitalmars-d

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.


Re: Fallback 'catch-all' template functions

2016-09-03 Thread Walter Bright via Digitalmars-d

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.


Re: Fallback 'catch-all' template functions

2016-09-02 Thread Manu via Digitalmars-d
On 3 September 2016 at 11:38, Andrei Alexandrescu via Digitalmars-d
 wrote:
> On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:
>>
>> On 3 September 2016 at 00:18, Xinok via Digitalmars-d
>>  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.



Re: Fallback 'catch-all' template functions

2016-09-02 Thread Walter Bright via Digitalmars-d

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.



Re: Fallback 'catch-all' template functions

2016-09-02 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 2:41 AM, Manu via Digitalmars-d wrote:

On 3 September 2016 at 00:18, Xinok via Digitalmars-d
 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


Re: Fallback 'catch-all' template functions

2016-09-02 Thread Manu via Digitalmars-d
On 3 September 2016 at 00:18, Xinok via Digitalmars-d
 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?


Re: Fallback 'catch-all' template functions

2016-09-02 Thread Xinok via Digitalmars-d

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 {}


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Meta via Digitalmars-d

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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Timon Gehr via Digitalmars-d

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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Cauterite via Digitalmars-d

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).


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Meta via Digitalmars-d

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.





Re: Fallback 'catch-all' template functions

2016-09-01 Thread Timon Gehr via Digitalmars-d

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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Meta via Digitalmars-d

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?


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Timon Gehr via Digitalmars-d

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));
}



Re: Fallback 'catch-all' template functions

2016-09-01 Thread Meta via Digitalmars-d
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)) {}


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Steven Schveighoffer via Digitalmars-d

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


Re: Fallback 'catch-all' template functions

2016-09-01 Thread H. S. Teoh via Digitalmars-d
On Thu, Sep 01, 2016 at 03:37:50PM +1000, 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.

Here's a possible workaround:

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

But this won't work across modules. :-(


T

-- 
The early bird gets the worm. Moral: ewww...


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Andrea Fontana via Digitalmars-d

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


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Ethan Watson via Digitalmars-d
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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Enamex via Digitalmars-d

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) { ... }


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Cauterite via Digitalmars-d
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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Manu via Digitalmars-d
On 1 September 2016 at 20:43, Dominikus Dittes Scherkl via
Digitalmars-d  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...


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Dominikus Dittes Scherkl via Digitalmars-d

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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Ethan Watson via Digitalmars-d
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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Dominikus Dittes Scherkl via Digitalmars-d
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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Dominikus Dittes Scherkl via Digitalmars-d

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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Manu via Digitalmars-d
On 1 September 2016 at 19:16, Stefan Koch via Digitalmars-d
 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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Stefan Koch via Digitalmars-d

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!




Re: Fallback 'catch-all' template functions

2016-09-01 Thread Manu via Digitalmars-d
On 1 September 2016 at 18:44, Stefan Koch via Digitalmars-d
 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.


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Andrej Mitrovic via Digitalmars-d
On 9/1/16, 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) {}
>

Best thing I can think of is to use a forwarding template:

-
import std.stdio;

// forwarding (or dispatching) template
void f(T)(T t)
{
static if(is(typeof( fImpl(t) )))  // found match
{
fImpl(t);
}
else  // no matches, use fallback
{
fallback(t);
}
}

void fImpl(T)(T t) if (is(T == int)) { writefln("Int: %s", t); }
void fImpl(T)(T t) if (is(T == float)) { writefln("Float: %s", t); }
void fallback(T)(T t) { writefln("Generic: %s", t); }

void main ( )
{
f(int(1));
f(float(1.0));
f("string");
}
-


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Stefan Koch via Digitalmars-d

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
   }
 }



Re: Fallback 'catch-all' template functions

2016-09-01 Thread Stefan Koch via Digitalmars-d

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
  }
}


Re: Fallback 'catch-all' template functions

2016-09-01 Thread Ethan Watson via Digitalmars-d

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.


Fallback 'catch-all' template functions

2016-08-31 Thread Manu via Digitalmars-d
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.