Re: "else if" for template constraints

2015-09-04 Thread Enamex via Digitalmars-d

On Friday, 4 September 2015 at 15:52:08 UTC, Enamex wrote:
The biggest problem, I think, is that a template can has 
multiple 'predicates' to agree to be instantiated, but only 
some of them can mutually exclusive (the specialization syntax 
produces mutually exclusive ones, the if-constraints don't).

[...]
template Bar(T) {
static if( is(T == int) || is(T == string) || ... ) {
//stuff
}
else static if( stuff ) {
// other stuff
}
else {
template return; // I know my T took whatever your type 
was but I actually don't match, please exclude me from your 
list for this instance...

}
}
[...]


On second thought, it wouldn't help as much as I'd thought with 
overloading problems. What we want is a 'which template has more 
'&&'ed expressions in its constraint?' which sounds pretty awful. 
I have no idea how this could work :/


Especially given that D's constraints are way more open than, 
say, Haskell's, in their checking; though ironically not their 
declaration/implementation (D is open because it checks for 
structural conformance of a struct instead of nominative & 
structural; but it's more restricted because it easily only 
checks for structure of the type as declared, with no way to 
attach recognized-to-the-constraint ad-hoc interfaces to a type 
like Haskell's with type-classes).


Re: "else if" for template constraints

2015-09-04 Thread Enamex via Digitalmars-d
On Monday, 17 August 2015 at 13:18:43 UTC, Steven Schveighoffer 
wrote:
I was just looking at fixing this 
bug:https://issues.dlang.org/show_bug.cgi?id=14925


[...]

How often are you writing overloaded templates, and you want to 
say "if it doesn't match anything else, do this"? I'd love to 
see some form of syntax that brings template constraints in 
line with tried-and-true if/else statements.


One way to do this is to lexically order the if constraints, 
and if any of them start with "else", then they are mutually 
exclusive with the immediately preceding constraint for the 
same symbol (just like normal else).


So for example, you'd have:

void replaceInPlace(T, Range)(ref T[] array, size_t from, 
size_t to, Range stuff)

if(isDynamicArray!Range &&
is(Unqual!(ElementEncodingType!Range) == T) &&
!is(T == const T) &&
!is(T == immutable T))
{ /* version 1 that tries to write into the array directly */ }

void replaceInPlace(T, Range)(ref T[] array, size_t from, 
size_t to,

Range stuff)
else if(is(typeof(replace(array, from, to, stuff
{ /* version 2, which simply forwards to replace */ }

looks much better IMO. Can we do something like this? I'm not a 
compiler guru, so I defer to you experts out there.


-Steve


The biggest problem, I think, is that a template can has multiple 
'predicates' to agree to be instantiated, but only some of them 
can mutually exclusive (the specialization syntax produces 
mutually exclusive ones, the if-constraints don't).


Thinking about it from this angle, I believe the most flexible 
and sensible solution would be to support a sort of "early 
return" from a template. Thus:


template Bar(T) {
static if( is(T == int) || is(T == string) || ... ) {
//stuff
}
else static if( stuff ) {
// other stuff
}
else {
template return; // I know my T took whatever your type 
was but I actually don't match, please exclude me from your list 
for this instance...

}
}

template Bar(T) {
static if( is(T == float) || is(T == int[]) || ... ) {
// Bar!float/Bar!(int[]) stuff
}
else static if( OTHER_OTHER_stuff ) {
// other other stuff
}
else {
template return; // I know my T took whatever your type 
was but I actually don't match, please exclude me from your list 
for this instance...

}
}


Re: else if for template constraints

2015-08-19 Thread Steven Schveighoffer via Digitalmars-d

On 8/17/15 6:44 PM, anonymous wrote:

On Monday, 17 August 2015 at 22:32:10 UTC, Idan Arye wrote:

On Monday, 17 August 2015 at 21:27:47 UTC, Meta wrote:

[...]

At that point, couldn't you just use static if inside the body of the
template instead of using template constraints?


No. Consider this: http://dpaste.dzfl.pl/a014aeba6e68. The having two
foo templates is illegal(though it'll only show when you try to
instantiate foo), because each of them covers all options for T. When
T is neither int nor float, the foo *function* in the first template
is not defined, but the *foo* template is still there.


The idea is to have only one template:

template foo(T) {
 static if (is(T == int)) {
 ...
 } else static if (is(T == float)) {
 ...
 } else static if (is(T == char)) {
 ...
 } else static if (is(T == bool)) {
 ...
 }
}



What if there is another foo template that handles double? possibly in 
another file? There would be a conflict for instantiation.


That is why we use template constraints instead of static if -- the 
constraint disqualifies the instantiation.


-Steve


Re: else if for template constraints

2015-08-17 Thread Idan Arye via Digitalmars-d

On Monday, 17 August 2015 at 21:27:47 UTC, Meta wrote:
On Monday, 17 August 2015 at 17:17:15 UTC, Steven Schveighoffer 
wrote:

On 8/17/15 1:00 PM, Idan Arye wrote:

It looks a bit ugly, that the `else` is after a function 
declaration
instead of directly after the if's then clause. How about 
doing it

with the full template style?

 template replaceInPlace(T, Range)
 if(isDynamicArray!Range 
 is(Unqual!(ElementEncodingType!Range) == T) 
 !is(T == const T) 
 !is(T == immutable T))
 {
 void replaceInPlace(ref T[] array, size_t from, 
size_t to,

Range stuff)
 { /* version 1 that tries to write into the array 
directly */ }

 }
 else if(is(typeof(replace(array, from, to, stuff
 {
 void replaceInPlace(ref T[] array, size_t from, 
size_t to,

Range stuff)
 { /* version 2, which simply forwards to replace */ }
 }


Yes, I like this much better.

-Steve


At that point, couldn't you just use static if inside the body 
of the template instead of using template constraints?


No. Consider this: http://dpaste.dzfl.pl/a014aeba6e68. The having 
two foo templates is illegal(though it'll only show when you try 
to instantiate foo), because each of them covers all options for 
T. When T is neither int nor float, the foo *function* in the 
first template is not defined, but the *foo* template is still 
there.


With the suggested syntax, the first foo template would only be 
defined for int and float, and the second will only be defined 
for char and bool - so there is no conflict.


Re: else if for template constraints

2015-08-17 Thread anonymous via Digitalmars-d

On Monday, 17 August 2015 at 22:32:10 UTC, Idan Arye wrote:

On Monday, 17 August 2015 at 21:27:47 UTC, Meta wrote:

[...]
At that point, couldn't you just use static if inside the body 
of the template instead of using template constraints?


No. Consider this: http://dpaste.dzfl.pl/a014aeba6e68. The 
having two foo templates is illegal(though it'll only show when 
you try to instantiate foo), because each of them covers all 
options for T. When T is neither int nor float, the foo 
*function* in the first template is not defined, but the *foo* 
template is still there.


The idea is to have only one template:

template foo(T) {
static if (is(T == int)) {
...
} else static if (is(T == float)) {
...
} else static if (is(T == char)) {
...
} else static if (is(T == bool)) {
...
}
}



Re: else if for template constraints

2015-08-17 Thread BBasile via Digitalmars-d

On Monday, 17 August 2015 at 22:44:15 UTC, anonymous wrote:

On Monday, 17 August 2015 at 22:32:10 UTC, Idan Arye wrote:

On Monday, 17 August 2015 at 21:27:47 UTC, Meta wrote:

[...]
At that point, couldn't you just use static if inside the 
body of the template instead of using template constraints?


No. Consider this: http://dpaste.dzfl.pl/a014aeba6e68. The 
having two foo templates is illegal(though it'll only show 
when you try to instantiate foo), because each of them covers 
all options for T. When T is neither int nor float, the foo 
*function* in the first template is not defined, but the *foo* 
template is still there.


The idea is to have only one template:

template foo(T) {
static if (is(T == int)) {
...
} else static if (is(T == float)) {
...
} else static if (is(T == char)) {
...
} else static if (is(T == bool)) {
...
}
}


There is also, as a similar option, the dispatcher solution, 
like for 'std.conv.to'


There is the main template that dispatches the call to the right 
non templated (or specialized) overload, so that the entry point 
is just used to redirect the cases that are fundamentaly 
different.


---
auto foo(T)
{
static if(...) return fooImpl!T(); else
static if(...) return fooImpl!T(); else
//etc
}

private auto fooImpl(T)(){}
private auto fooImpl(T)(){}
private auto fooImpl(T)(){}
---




Re: else if for template constraints

2015-08-17 Thread Steven Schveighoffer via Digitalmars-d

On 8/17/15 9:18 AM, Steven Schveighoffer wrote:


The issue (as I noted in the bug report), is that the array being
replaced is some string, and the element type of the stuff to replace
is a dchar. But the first version is better for replacing a char[] in a
char[], and works just fine.


I guess this was a slightly wrong opinion. Because the first overload 
uses remove, and remove cannot work with char[] (see phobos 
schizophrenia regarding strings), it must call the always-allocating 
second version.


In any case, the template constraints are still simpler with the new 
style (if(!priorconstraints)  ...)


-Steve


else if for template constraints

2015-08-17 Thread Steven Schveighoffer via Digitalmars-d
I was just looking at fixing this 
bug:https://issues.dlang.org/show_bug.cgi?id=14925


A little background for the root cause:

replaceInPlace has 2 versions. One is a specialized version that 
replaces the actual elements in an array with another array of the same 
type.


The second version just uses replace, and then overwrites the original 
array reference. This is used when the stuff to replace is not an array, 
or the array elements don't match, or the target array has const or 
immutable elements.


The constraint for version 1 is:

if(isDynamicArray!Range 
   is(ElementEncodingType!Range : T) 
   !is(T == const T) 
   !is(T == immutable T))

More on that later. The constraint for version 2 is:

if(isInputRange!Range 
   ((!isDynamicArray!Range  is(ElementType!Range : T)) ||
(isDynamicArray!Range  is(ElementType!Range : T) 
 (is(T == const T) || is(T == immutable T))) ||
isSomeString!(T[])  is(ElementType!Range : dchar)))

The issue (as I noted in the bug report), is that the array being 
replaced is some string, and the element type of the stuff to replace 
is a dchar. But the first version is better for replacing a char[] in a 
char[], and works just fine.


So I set about fixing this third constraint. We need to only call this 
version if the some string actually has non-mutable characters. So I 
proceeded to add another is(T == const T) || is(T == immutable T), but 
then I realized, wait, the first version will also be called if the 
ElementEncodingType of Range *fits into* a T (note the use of a colon 
instead of ==). Therefore, replaceInPlace!(wchar, char[]) will 
incorrectly try and call the first version, when it should call the second.


Therefore, I needed to update the constraints on the first version. This 
screws up the whole dynamic, because both constraints are evaluated 
INDEPENDENTLY. You can't have any overlap, so any changes to the first 
constraint may cause issues with the second (and in this case, it does). 
My second set of constraints was starting to look REALLY complicated.


What I really need for the second constraint is: doesn't match the 
first version AND I can call replace with those arguments.


So that's what I did:

if(!(... /* whole constraint from first version */)  
is(typeof(replace(array, from, to, stuff


This is not very DRY. One thing I could do is factor out the main 
constraint into another template:


enum _replaceInPlaceConstraint(T, Range) = ...

Great! But then the docs don't reflect the true constraint (they just 
have this _replaceInPlaceConstraint, er.. constraint), and I've 
contributed to the ever-growing template bloat of phobos.


How often are you writing overloaded templates, and you want to say if 
it doesn't match anything else, do this? I'd love to see some form of 
syntax that brings template constraints in line with tried-and-true 
if/else statements.


One way to do this is to lexically order the if constraints, and if any 
of them start with else, then they are mutually exclusive with the 
immediately preceding constraint for the same symbol (just like normal 
else).


So for example, you'd have:

void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, 
Range stuff)

if(isDynamicArray!Range 
is(Unqual!(ElementEncodingType!Range) == T) 
!is(T == const T) 
!is(T == immutable T))
{ /* version 1 that tries to write into the array directly */ }

void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to,
Range stuff)
else if(is(typeof(replace(array, from, to, stuff
{ /* version 2, which simply forwards to replace */ }

looks much better IMO. Can we do something like this? I'm not a compiler 
guru, so I defer to you experts out there.


-Steve


Re: else if for template constraints

2015-08-17 Thread Timon Gehr via Digitalmars-d

On 08/17/2015 03:18 PM, Steven Schveighoffer wrote:

Can we do something like this? I'm not a compiler guru, so I defer to
you experts out there.


Implementation is trivial. (A naive implementation strategy which works 
is to just use the obvious lowering.)


Re: else if for template constraints

2015-08-17 Thread Idan Arye via Digitalmars-d
On Monday, 17 August 2015 at 13:18:43 UTC, Steven Schveighoffer 
wrote:
void replaceInPlace(T, Range)(ref T[] array, size_t from, 
size_t to, Range stuff)

if(isDynamicArray!Range 
is(Unqual!(ElementEncodingType!Range) == T) 
!is(T == const T) 
!is(T == immutable T))
{ /* version 1 that tries to write into the array directly */ }

void replaceInPlace(T, Range)(ref T[] array, size_t from, 
size_t to,

Range stuff)
else if(is(typeof(replace(array, from, to, stuff
{ /* version 2, which simply forwards to replace */ }


It looks a bit ugly, that the `else` is after a function 
declaration instead of directly after the if's then clause. How 
about doing it with the full template style?


template replaceInPlace(T, Range)
if(isDynamicArray!Range 
is(Unqual!(ElementEncodingType!Range) == T) 
!is(T == const T) 
!is(T == immutable T))
{
void replaceInPlace(ref T[] array, size_t from, size_t 
to, Range stuff)
{ /* version 1 that tries to write into the array 
directly */ }

}
else if(is(typeof(replace(array, from, to, stuff
{
void replaceInPlace(ref T[] array, size_t from, size_t 
to, Range stuff)

{ /* version 2, which simply forwards to replace */ }
}


Re: else if for template constraints

2015-08-17 Thread Zoadian via Digitalmars-d

On Monday, 17 August 2015 at 16:57:33 UTC, Zoadian wrote:
wouldn't is(typeof(replace(array, from, to, stuff))) better be 
a static if inside the first version?


nevermind, I missed that the first constraint is negated.

In that case I agree, else if would be nice.


Re: else if for template constraints

2015-08-17 Thread Zoadian via Digitalmars-d
On Monday, 17 August 2015 at 13:18:43 UTC, Steven Schveighoffer 
wrote:
void replaceInPlace(T, Range)(ref T[] array, size_t from, 
size_t to, Range stuff)

if(isDynamicArray!Range 
is(Unqual!(ElementEncodingType!Range) == T) 
!is(T == const T) 
!is(T == immutable T))
{ /* version 1 that tries to write into the array directly */ }

void replaceInPlace(T, Range)(ref T[] array, size_t from, 
size_t to,

Range stuff)
else if(is(typeof(replace(array, from, to, stuff
{ /* version 2, which simply forwards to replace */ }

looks much better IMO. Can we do something like this? I'm not a 
compiler guru, so I defer to you experts out there.


-Steve


wouldn't is(typeof(replace(array, from, to, stuff))) better be a 
static if inside the first version?


Re: else if for template constraints

2015-08-17 Thread Steven Schveighoffer via Digitalmars-d

On 8/17/15 1:00 PM, Idan Arye wrote:


It looks a bit ugly, that the `else` is after a function declaration
instead of directly after the if's then clause. How about doing it
with the full template style?

 template replaceInPlace(T, Range)
 if(isDynamicArray!Range 
 is(Unqual!(ElementEncodingType!Range) == T) 
 !is(T == const T) 
 !is(T == immutable T))
 {
 void replaceInPlace(ref T[] array, size_t from, size_t to,
Range stuff)
 { /* version 1 that tries to write into the array directly */ }
 }
 else if(is(typeof(replace(array, from, to, stuff
 {
 void replaceInPlace(ref T[] array, size_t from, size_t to,
Range stuff)
 { /* version 2, which simply forwards to replace */ }
 }


Yes, I like this much better.

-Steve


Re: else if for template constraints

2015-08-17 Thread Meta via Digitalmars-d
On Monday, 17 August 2015 at 17:17:15 UTC, Steven Schveighoffer 
wrote:

On 8/17/15 1:00 PM, Idan Arye wrote:

It looks a bit ugly, that the `else` is after a function 
declaration
instead of directly after the if's then clause. How about 
doing it

with the full template style?

 template replaceInPlace(T, Range)
 if(isDynamicArray!Range 
 is(Unqual!(ElementEncodingType!Range) == T) 
 !is(T == const T) 
 !is(T == immutable T))
 {
 void replaceInPlace(ref T[] array, size_t from, 
size_t to,

Range stuff)
 { /* version 1 that tries to write into the array 
directly */ }

 }
 else if(is(typeof(replace(array, from, to, stuff
 {
 void replaceInPlace(ref T[] array, size_t from, 
size_t to,

Range stuff)
 { /* version 2, which simply forwards to replace */ }
 }


Yes, I like this much better.

-Steve


At that point, couldn't you just use static if inside the body of 
the template instead of using template constraints?