www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - TDPL: Overloading template functions

reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
There are two templated functions that overload against each other, and should
pass the unittests. But there are a few problems. Here's the example code:

T[] find(T, E)(T[] haystack, E needle) 
    if (is(typeof(haystack[0] != needle) == bool))
{
    while (haystack.length > 0 && haystack[0] != needle) {
        haystack = haystack[1 .. $];
    }
    return haystack;
}

T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (is(typeof(longer[0 .. 1] == shorter) : bool))
{
    while (longer.length >= shorter.length) {
        if (longer[0 .. shorter.length] == shorter)
            break;
        longer = longer[1 .. $];
    }
    return longer;
}

unittest {
    double[] d1 = [6.0, 1.5, 2.4, 3];
    float[] d2 = [1.5, 2.4];
    assert(find(d1, d2) == d1[1 .. $]);
}

unittest {
    double[] d = [1.5, 2.4];
    assert(find(d, 1.0) == null);
    assert(find(d, 1.5) == d);
    
    string[] s = ["one", "two"];
    assert(find(s, "two") == ["two"]);
}

The first unittest will fail. There is some funky issue going around here. This
small code will pass the unittest:

import std.stdio;

void main() {
}

float x = 4.0;
double y = 4.0;

unittest {
    assert(x == y);
}

But this one will not:

import std.stdio;

void main() {
}

float x = 4.2;
double y = 4.2;

unittest {
    assert(x == y);
}

Notice I've only changed the 0's to a different number. So, if I change the
first unittest in the original code to this:

unittest {
    double[] d1 = [6.0, 1.0, 2.0, 3];
    float[] d2 = [1.0, 2.0];
    assert(find(d1, d2) == d1[1 .. $]);
}

It will pass. I guess this has something to do with internal representation of
floats/doubles..

The second unittest from the Example code, which I'm pasting here again for
convenience:

unittest {
    double[] d = [1.5, 2.4];
    assert(find(d, 1.0) == null);
    assert(find(d, 1.5) == d);
    
    string[] s = ["one", "two"];
    assert(find(s, "two") == ["two"]);
}

Gives a compiler error saying that both of the overloaded templated functions
match for the call (the last assert in the unittest).

Also, Andrei, you never explained the if(is()) signature of the second
templated function. I hope to get some pointers on that. :)
Jul 27 2010
next sibling parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic
<andrej.mitrovich gmail.com>wrote:

I won't comment on the double/float issue, I do not anything about it.


 T[] find(T, E)(T[] haystack, E needle)
    if (is(typeof(haystack[0] != needle) == bool))

 T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (is(typeof(longer[0 .. 1] == shorter) : bool))

 Also, Andrei, you never explained the if(is()) signature of the second
 templated function. I hope to get some pointers on that. :)
I'll have a try at this one. As you may know, the is(typeof()) syntax is a way to try to compile an expression and see if it works. If it works, it has a type, given by typeof and is(Type) returns true. So the first one is really saying: "Hey compiler, may I compare an element of haystack (of type T) with an E In this case, longer is an array. So longer[0] would be an element, a T1. There is no chance that shorter will match as an element cannot be equal to an array ... except, of course, if longer is an array of T2[] (T1 == T2[]). Longer is a T2[][]. That may happen for arrays of strings, strings being arrays of chars. And I think that's the problem you have in your last unit test. Anyway, he takes longer[0..1] to get a slice, which is a dynamic array, a T1[] and hence comparable to a T2[]... most of the time. As for using ': bool' instead of '== bool' as in the first find(), I don't think there is any difference in this case. I understand T == U as 'T is the exact same type as U', whereas T : U is for me "T is a subtype of U". But then, a subtype of bool is pretty much constrained to be a bool. The T : U syntax is pretty much only used for classes : if(is(T : MyClass)) is saying : 'compile this only if T is a subclass of MyClass'. Philippe , except if longer is in fact
Jul 28 2010
parent Ryan W Sims <rwsims gmail.com> writes:
On 7/28/10 7:44 AM, Philippe Sigaud wrote:
 On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic
 <andrej.mitrovich gmail.com <mailto:andrej.mitrovich gmail.com>> wrote:

 I won't comment on the double/float issue, I do not anything about it.

     T[] find(T, E)(T[] haystack, E needle)
         if (is(typeof(haystack[0] != needle) == bool))

     T1[] find(T1, T2)(T1[] longer, T2[] shorter)
         if (is(typeof(longer[0 .. 1] == shorter) : bool))

     Also, Andrei, you never explained the if(is()) signature of the
     second templated function. I hope to get some pointers on that. :)
If I understand the 'is' statement correctly, wouldn't this be a little less verbose and still the same semantics? T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle)) { ...do the find... } (i.e. just drop the "== bool" from the guard?) When I compiled it, assert(find([1.0, 2.0], 2) == [2.0]) compiled fine, and assert(find([1, 2], "2") == []) didn't which is expected. Is there something I'm missing? -- rwsims
 I'll have a try at this one.
 As you may know, the is(typeof()) syntax is a way to try to compile an
 expression and see if it works. If it works, it has a type, given by
 typeof and is(Type) returns true. So the first one is really saying:
 "Hey compiler, may I compare an element of haystack (of type T) with an E
 In this case, longer is an array. So longer[0] would be an element, a
 T1. There is no chance that shorter will match as an element cannot be
 equal to an array ... except, of course, if longer is an array of T2[]
   (T1 == T2[]). Longer is a T2[][]. That may happen for arrays of
 strings, strings being arrays of chars. And I think that's the problem
 you have in your last unit test.

 Anyway, he takes longer[0..1] to get a slice, which is a dynamic array,
 a T1[] and hence comparable to a T2[]... most of the time.

 As for using ': bool' instead of '== bool' as in the first find(),  I
 don't think there is any difference in this case. I understand T == U as
 'T is the exact same type as U', whereas T : U is for me "T is a subtype
 of U". But then, a subtype of bool is pretty much constrained to be a
 bool. The T : U syntax is pretty much only used for classes : if(is(T :
 MyClass)) is saying : 'compile this only if T is a subclass of MyClass'.


 Philippe



 , except if longer is in fact
Aug 01 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
These templates seem to be hard to get right. I guess with experience they
get easier to write and comprehend. Anyways, thanks for the explanation.

On Wed, Jul 28, 2010 at 4:44 PM, Philippe Sigaud
<philippe.sigaud gmail.com>wrote:

 On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
I won't comment on the double/float issue, I do not anything about it.
 T[] find(T, E)(T[] haystack, E needle)
    if (is(typeof(haystack[0] != needle) == bool))

 T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (is(typeof(longer[0 .. 1] == shorter) : bool))

 Also, Andrei, you never explained the if(is()) signature of the second
 templated function. I hope to get some pointers on that. :)
I'll have a try at this one. As you may know, the is(typeof()) syntax is a way to try to compile an expression and see if it works. If it works, it has a type, given by typeof and is(Type) returns true. So the first one is really saying: "Hey compiler, may I compare an element of haystack (of type T) with an E In this case, longer is an array. So longer[0] would be an element, a T1. There is no chance that shorter will match as an element cannot be equal to an array ... except, of course, if longer is an array of T2[] (T1 == T2[]). Longer is a T2[][]. That may happen for arrays of strings, strings being arrays of chars. And I think that's the problem you have in your last unit test. Anyway, he takes longer[0..1] to get a slice, which is a dynamic array, a T1[] and hence comparable to a T2[]... most of the time. As for using ': bool' instead of '== bool' as in the first find(), I don't think there is any difference in this case. I understand T == U as 'T is the exact same type as U', whereas T : U is for me "T is a subtype of U". But then, a subtype of bool is pretty much constrained to be a bool. The T : U syntax is pretty much only used for classes : if(is(T : MyClass)) is saying : 'compile this only if T is a subclass of MyClass'. Philippe , except if longer is in fact
Jul 28 2010
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Andrej Mitrovic wrote:
 Also, Andrei, you never explained the if(is()) signature of the second
templated function. I hope to get some pointers on that. :)
Is the explanation on page 141 adequate? Andrei
Jul 28 2010
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
It was adequate for the first function, but I never knew why you've changed
"== bool" to ": bool", and why you're using slices in the second templated
function. Philippe explained it in more detail so I'm starting to get it.

Otherwise, the only issue left is the float vs double comparisons.

For this unittest:
    double[] d1 = [6.0, 1.5, 2.4, 3];
    float[] d2 = [1.5, 2.4];
    assert(find(d1, d2) == d1[1 .. $]);

Inside the second template function I've added a writeln, and I can see the
if statement evaluates "[1.5, 2.4] == [1.5, 2.4]" to false and will not
break out of the loop. I don't know whether this is a DMD bug or if "float[]
d2" should be changed to "double[] d2" in TDPL, since that runs fine. IIRC
arrays are value types and as such they are compared bit-by-bit to each
other, right? And since doubles and floats have different sizes, that
unittest then fails.


On Wed, Jul 28, 2010 at 6:19 PM, Andrei Alexandrescu <
SeeWebsiteForEmail erdani.org> wrote:

 Andrej Mitrovic wrote:

 Also, Andrei, you never explained the if(is()) signature of the second
 templated function. I hope to get some pointers on that. :)
Is the explanation on page 141 adequate? Andrei
Jul 28 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:
 IIRC arrays are value types and as such they are compared bit-by-bit
 to each other, right?
Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Woops, I got confused. I was thinking about structs, not arrays.

But yeah, in this case a float gets compared to a double and it seems to
evaluate to false.

On Wed, Jul 28, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.com>wrote:

 On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:
 IIRC arrays are value types and as such they are compared bit-by-bit
 to each other, right?
Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
parent reply Rainer Schuetze <r.sagitario gmx.de> writes:
Andrej Mitrovic wrote:
 Woops, I got confused. I was thinking about structs, not arrays.
 
 But yeah, in this case a float gets compared to a double and it seems to 
 evaluate to false.
2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 28 2010
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Should I just keep posting my findings in this thread? I hate to make a new
thread for every problem I find..

Anyway, on Page 150, this code fails:

auto f = (int i) {};
assert(is(f == function));

I've checked via typeid(), and f is a delegate by default, not a function.
I've tested it both in a function's scope and in module scope, with the same
results.

On Wed, Jul 28, 2010 at 10:56 PM, Rainer Schuetze <r.sagitario gmx.de>wrote:

 Andrej Mitrovic wrote:

 Woops, I got confused. I was thinking about structs, not arrays.


 But yeah, in this case a float gets compared to a double and it seems to
 evaluate to false.
2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 28 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I can't get the example on page 158 to compile, I've tried both versions of
reduce:

import std.stdio, std.range;

 property bool empty(T)(T[] a) { return a.length == 0; }
 property ref T front(T)(T[] a) { return a[0]; }
void popFront(T)(ref T[] a) { a = a[1 .. $]; }

V reduce(alias fun, V, R)(V x, R range)
    if (isInputRange!R && is(typeof(x = fun(x, range.front))))
{
    for (; !range.empty; range.popFront()) {
        x = fun(x, range.front);
    }
    return x;
}

unittest {
    int[] r = [10, 14, 3, 5, 23];

    // compute the sum of all elements
    int sum = reduce!((a, b) { return a + b; })(0, r);
    assert(sum == 55);

    // compute minimum
    int min = reduce!((a, b) { return a < b ? a : b; })(r[0], r);
    assert(min == 3);
}

void main() { }

Errors:
reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if
(isInputRange!(R) && is(typeof(x = fun(x,range.front)))) does not match any
function template declaration

reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if
(isInputRange!(R) && is(typeof(x = fun(x,range.front)))) cannot deduce
template function from argument types !(__dgliteral1)(int,int[])

reduce.d(31): Error: template instance errors instantiating template

If I comment out the template constraints (the if statements for either
version of reduce), the example will compile and the unittests will pass.

On Wed, Jul 28, 2010 at 11:15 PM, Andrej Mitrovic <
andrej.mitrovich gmail.com> wrote:

 Should I just keep posting my findings in this thread? I hate to make a new
 thread for every problem I find..

 Anyway, on Page 150, this code fails:

 auto f = (int i) {};
 assert(is(f == function));

 I've checked via typeid(), and f is a delegate by default, not a function.
 I've tested it both in a function's scope and in module scope, with the same
 results.


 On Wed, Jul 28, 2010 at 10:56 PM, Rainer Schuetze <r.sagitario gmx.de>wrote:

 Andrej Mitrovic wrote:

 Woops, I got confused. I was thinking about structs, not arrays.


 But yeah, in this case a float gets compared to a double and it seems to
 evaluate to false.
2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 28 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Page 160, code:

import std.algorithm, std.range;

void main() {}

double average() { return 1.0; }
double average(double) { return 2.0; }

// computes average of a set of numbers, passable directly or via an array
double average(double[] values...) {
    if (values.empty) {
        throw new Exception("Average of zero elements in undefined");
    }
    return reduce!((a, b) { return a + b; })(0.0, values) / values.length;
}

unittest {
    average();
    average(2.5);
}


The book states this is an ambiguous call, but I can succesfully compile.

On Thu, Jul 29, 2010 at 4:16 AM, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 I can't get the example on page 158 to compile, I've tried both versions of
 reduce:

 import std.stdio, std.range;

  property bool empty(T)(T[] a) { return a.length == 0; }
  property ref T front(T)(T[] a) { return a[0]; }
 void popFront(T)(ref T[] a) { a = a[1 .. $]; }

 V reduce(alias fun, V, R)(V x, R range)
     if (isInputRange!R && is(typeof(x = fun(x, range.front))))
 {
     for (; !range.empty; range.popFront()) {
         x = fun(x, range.front);
     }
     return x;
 }

 unittest {
     int[] r = [10, 14, 3, 5, 23];

     // compute the sum of all elements
     int sum = reduce!((a, b) { return a + b; })(0, r);
     assert(sum == 55);

     // compute minimum
     int min = reduce!((a, b) { return a < b ? a : b; })(r[0], r);
     assert(min == 3);
 }

 void main() { }

 Errors:
 reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if
 (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) does not match any
 function template declaration

 reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if
 (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) cannot deduce
 template function from argument types !(__dgliteral1)(int,int[])

 reduce.d(31): Error: template instance errors instantiating template

 If I comment out the template constraints (the if statements for either
 version of reduce), the example will compile and the unittests will pass.


 On Wed, Jul 28, 2010 at 11:15 PM, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 Should I just keep posting my findings in this thread? I hate to make a
 new thread for every problem I find..

 Anyway, on Page 150, this code fails:

 auto f = (int i) {};
 assert(is(f == function));

 I've checked via typeid(), and f is a delegate by default, not a function.
 I've tested it both in a function's scope and in module scope, with the same
 results.


 On Wed, Jul 28, 2010 at 10:56 PM, Rainer Schuetze <r.sagitario gmx.de>wrote:

 Andrej Mitrovic wrote:

 Woops, I got confused. I was thinking about structs, not arrays.


 But yeah, in this case a float gets compared to a double and it seems to
 evaluate to false.
2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 29 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
How do I print out an expanded tuple but with spaces between the values?
There's this example on page 164:

import std.typecons, std.stdio;

void fun(T...)(T args) {
    // create a tuple to pack all arguments together
    gun(tuple(args));
}

void gun(T)(T value) {
    // expand the tuple back
    writeln(value.expand);
}

void main() {
    fun(1);
    fun(1, 2.2);
}

This prints out:
1
12.2

But that's confusing, since the values are "glued" together when printed
out. I really want to print out:
1
1 2.2

Any ideas?

One other thing. I can use the .length property for value and parameter
tuples, but only if I haven't built them myself with the call to
std.typecons.tuple(). For example I can call writeln(T.length) in the fun()
function (where the compiler automatically constructs a tuple), but not in
the gun() function. So there seems to be compiler tuples and
user-constructed tuples, which are not the same.

It's a bit of a shame that there isn't a chapter on tuples except this brief
mention (I don't see it listed in the contents page). I guess I'll take a
look at the implementation.

On Thu, Jul 29, 2010 at 5:08 PM, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 Page 160, code:

 import std.algorithm, std.range;

 void main() {}

 double average() { return 1.0; }
 double average(double) { return 2.0; }

 // computes average of a set of numbers, passable directly or via an array
 double average(double[] values...) {
     if (values.empty) {
         throw new Exception("Average of zero elements in undefined");
     }
     return reduce!((a, b) { return a + b; })(0.0, values) / values.length;
 }

 unittest {
     average();
     average(2.5);
 }


 The book states this is an ambiguous call, but I can succesfully compile.


 On Thu, Jul 29, 2010 at 4:16 AM, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 I can't get the example on page 158 to compile, I've tried both versions
 of reduce:

 import std.stdio, std.range;

  property bool empty(T)(T[] a) { return a.length == 0; }
  property ref T front(T)(T[] a) { return a[0]; }
 void popFront(T)(ref T[] a) { a = a[1 .. $]; }

 V reduce(alias fun, V, R)(V x, R range)
     if (isInputRange!R && is(typeof(x = fun(x, range.front))))
 {
     for (; !range.empty; range.popFront()) {
         x = fun(x, range.front);
     }
     return x;
 }

 unittest {
     int[] r = [10, 14, 3, 5, 23];

     // compute the sum of all elements
     int sum = reduce!((a, b) { return a + b; })(0, r);
     assert(sum == 55);

     // compute minimum
     int min = reduce!((a, b) { return a < b ? a : b; })(r[0], r);
     assert(min == 3);
 }

 void main() { }

 Errors:
 reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if
 (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) does not match any
 function template declaration

 reduce.d(31): Error: template reduce.reduce(alias fun,V,R) if
 (isInputRange!(R) && is(typeof(x = fun(x,range.front)))) cannot deduce
 template function from argument types !(__dgliteral1)(int,int[])

 reduce.d(31): Error: template instance errors instantiating template

 If I comment out the template constraints (the if statements for either
 version of reduce), the example will compile and the unittests will pass.


 On Wed, Jul 28, 2010 at 11:15 PM, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 Should I just keep posting my findings in this thread? I hate to make a
 new thread for every problem I find..

 Anyway, on Page 150, this code fails:

 auto f = (int i) {};
 assert(is(f == function));

 I've checked via typeid(), and f is a delegate by default, not a
 function. I've tested it both in a function's scope and in module scope,
 with the same results.


 On Wed, Jul 28, 2010 at 10:56 PM, Rainer Schuetze <r.sagitario gmx.de>wrote:

 Andrej Mitrovic wrote:

 Woops, I got confused. I was thinking about structs, not arrays.


 But yeah, in this case a float gets compared to a double and it seems
 to evaluate to false.
2.4 has no exact representation as a floating point value, as it is 2^^2*0.6, so the mantissa is 3/5. It is rounded after 24 bits for float, but after 53 bits for a double. As a result float f = 2.4; // approximately 2.4000001 double d = 2.4; // approximately 2.3999999999999999 assert(f == d); fails, because the comparison is done after converting the truncated float to double (as long as the compiler does not issue SSE instructions with single precision). In contrast, this assert(2.4 == 2.4f); passes, as the compiler keeps real precision until writing the value to memory and evaluates the expression at compile time.
Jul 29 2010
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic
<andrej.mitrovich gmail.com>wrote:

 How do I print out an expanded tuple but with spaces between the values?
 There's this example on page 164:

 import std.typecons, std.stdio;

 void fun(T...)(T args) {
     // create a tuple to pack all arguments together
     gun(tuple(args));
 }

 void gun(T)(T value) {
     // expand the tuple back
     writeln(value.expand);
 }

 void main() {
     fun(1);
     fun(1, 2.2);
 }

 This prints out:
 1
 12.2

 But that's confusing, since the values are "glued" together when printed
 out. I really want to print out:
 1
 1 2.2

 Any ideas?
You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.
 One other thing. I can use the .length property for value and parameter
 tuples, but only if I haven't built them myself with the call to
 std.typecons.tuple(). For example I can call writeln(T.length) in the fun()
 function (where the compiler automatically constructs a tuple), but not in
 the gun() function. So there seems to be compiler tuples and
 user-constructed tuples, which are not the same.
Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);
 It's a bit of a shame that there isn't a chapter on tuples except this
 brief mention (I don't see it listed in the contents page). I guess I'll
 take a look at the implementation.
Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Thanks Phillippe, that explains a lot.

I like to try out all the examples, I never take any of it for granted. But
hardly any book comes with 100% working examples, even the much praised CPL
is loaded with syntax mistakes. With that said I have to admit I'm really
enjoying reading TDPL so far.

On Fri, Jul 30, 2010 at 12:11 PM, Philippe Sigaud <philippe.sigaud gmail.com
 wrote:
 On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 How do I print out an expanded tuple but with spaces between the values?
 There's this example on page 164:

 import std.typecons, std.stdio;

 void fun(T...)(T args) {
     // create a tuple to pack all arguments together
     gun(tuple(args));
 }

 void gun(T)(T value) {
     // expand the tuple back
     writeln(value.expand);
 }

 void main() {
     fun(1);
     fun(1, 2.2);
 }

 This prints out:
 1
 12.2

 But that's confusing, since the values are "glued" together when printed
 out. I really want to print out:
 1
 1 2.2

 Any ideas?
You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.
 One other thing. I can use the .length property for value and parameter
 tuples, but only if I haven't built them myself with the call to
 std.typecons.tuple(). For example I can call writeln(T.length) in the fun()
 function (where the compiler automatically constructs a tuple), but not in
 the gun() function. So there seems to be compiler tuples and
 user-constructed tuples, which are not the same.
Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);
 It's a bit of a shame that there isn't a chapter on tuples except this
 brief mention (I don't see it listed in the contents page). I guess I'll
 take a look at the implementation.
Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Hey, just tried out your code.

In the first one there's a little typo (used 'i' instead of index), but
that's ok. ;p

It seems like I don't need to use the to!() template. This code seems to
work fine:

void gun(T)(T value) {
    foreach(Type; value.expand)
        write(Type, " ");

    writeln();
}

But regardless, at least I now know how to traverse through my own tuples,
so thanks for that one.

In your second example:

Tuple!("index", int, "sum", double) t = tuple(1,3.14);

assert(t.index== 1); // t has .first and .second as members.
assert(t.sum== 3.14);

I get an error way deep in the library itself (this could call for a better
error message, methinks):
C:\DMD\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(320): Error: tuple
index 2 exceeds 2

I've reversed the order, put types before identifiers and this works fine
then:

Tuple!(int, "index", double, "sum") t = tuple(1, 3.14);

I'm not sure what you mean by "t has .first and .second as members", I can't
seem to access those. Maybe you meant t.index and t.sum as members?


On Fri, Jul 30, 2010 at 12:11 PM, Philippe Sigaud <philippe.sigaud gmail.com
 wrote:
 On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 How do I print out an expanded tuple but with spaces between the values?
 There's this example on page 164:

 import std.typecons, std.stdio;

 void fun(T...)(T args) {
     // create a tuple to pack all arguments together
     gun(tuple(args));
 }

 void gun(T)(T value) {
     // expand the tuple back
     writeln(value.expand);
 }

 void main() {
     fun(1);
     fun(1, 2.2);
 }

 This prints out:
 1
 12.2

 But that's confusing, since the values are "glued" together when printed
 out. I really want to print out:
 1
 1 2.2

 Any ideas?
You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.
 One other thing. I can use the .length property for value and parameter
 tuples, but only if I haven't built them myself with the call to
 std.typecons.tuple(). For example I can call writeln(T.length) in the fun()
 function (where the compiler automatically constructs a tuple), but not in
 the gun() function. So there seems to be compiler tuples and
 user-constructed tuples, which are not the same.
Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);
 It's a bit of a shame that there isn't a chapter on tuples except this
 brief mention (I don't see it listed in the contents page). I guess I'll
 take a look at the implementation.
Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I've found that the syntax mytuple.field[] can be used, I think this is
probably what you meant by .first and .second.


On Fri, Jul 30, 2010 at 8:20 PM, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 Hey, just tried out your code.

 In the first one there's a little typo (used 'i' instead of index), but
 that's ok. ;p

 It seems like I don't need to use the to!() template. This code seems to
 work fine:


 void gun(T)(T value) {
     foreach(Type; value.expand)
         write(Type, " ");

     writeln();
 }

 But regardless, at least I now know how to traverse through my own tuples,
 so thanks for that one.

 In your second example:


 Tuple!("index", int, "sum", double) t = tuple(1,3.14);

 assert(t.index== 1); // t has .first and .second as members.
 assert(t.sum== 3.14);

 I get an error way deep in the library itself (this could call for a better
 error message, methinks):
 C:\DMD\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(320): Error: tuple
 index 2 exceeds 2

 I've reversed the order, put types before identifiers and this works fine
 then:

 Tuple!(int, "index", double, "sum") t = tuple(1, 3.14);

 I'm not sure what you mean by "t has .first and .second as members", I
 can't seem to access those. Maybe you meant t.index and t.sum as members?


 On Fri, Jul 30, 2010 at 12:11 PM, Philippe Sigaud <
 philippe.sigaud gmail.com> wrote:

 On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 How do I print out an expanded tuple but with spaces between the values?
 There's this example on page 164:

 import std.typecons, std.stdio;

 void fun(T...)(T args) {
     // create a tuple to pack all arguments together
     gun(tuple(args));
 }

 void gun(T)(T value) {
     // expand the tuple back
     writeln(value.expand);
 }

 void main() {
     fun(1);
     fun(1, 2.2);
 }

 This prints out:
 1
 12.2

 But that's confusing, since the values are "glued" together when printed
 out. I really want to print out:
 1
 1 2.2

 Any ideas?
You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.
 One other thing. I can use the .length property for value and parameter
 tuples, but only if I haven't built them myself with the call to
 std.typecons.tuple(). For example I can call writeln(T.length) in the fun()
 function (where the compiler automatically constructs a tuple), but not in
 the gun() function. So there seems to be compiler tuples and
 user-constructed tuples, which are not the same.
Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);
 It's a bit of a shame that there isn't a chapter on tuples except this
 brief mention (I don't see it listed in the contents page). I guess I'll
 take a look at the implementation.
Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Speaking of which, I think there's a typo in Phobos docs:

http://www.digitalmars.com/d/2.0/phobos/std_typecons.html

Tuple!(int, int) point;
// assign coordinates
point.field[0] = 5;
point.field[1] = 6;
// read coordinates
auto x = point.field[0];
auto y = point.[1];

The last one should be "auto y = point.field[1]"

On Fri, Jul 30, 2010 at 8:53 PM, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 I've found that the syntax mytuple.field[] can be used, I think this is
 probably what you meant by .first and .second.


 On Fri, Jul 30, 2010 at 8:20 PM, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 Hey, just tried out your code.

 In the first one there's a little typo (used 'i' instead of index), but
 that's ok. ;p

 It seems like I don't need to use the to!() template. This code seems to
 work fine:


 void gun(T)(T value) {
     foreach(Type; value.expand)
         write(Type, " ");

     writeln();
 }

 But regardless, at least I now know how to traverse through my own tuples,
 so thanks for that one.

 In your second example:


 Tuple!("index", int, "sum", double) t = tuple(1,3.14);

 assert(t.index== 1); // t has .first and .second as members.
 assert(t.sum== 3.14);

 I get an error way deep in the library itself (this could call for a
 better error message, methinks):
 C:\DMD\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(320): Error: tuple
 index 2 exceeds 2

 I've reversed the order, put types before identifiers and this works fine
 then:

 Tuple!(int, "index", double, "sum") t = tuple(1, 3.14);

 I'm not sure what you mean by "t has .first and .second as members", I
 can't seem to access those. Maybe you meant t.index and t.sum as members?


 On Fri, Jul 30, 2010 at 12:11 PM, Philippe Sigaud <
 philippe.sigaud gmail.com> wrote:

 On Thu, Jul 29, 2010 at 23:28, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 How do I print out an expanded tuple but with spaces between the values?
 There's this example on page 164:

 import std.typecons, std.stdio;

 void fun(T...)(T args) {
     // create a tuple to pack all arguments together
     gun(tuple(args));
 }

 void gun(T)(T value) {
     // expand the tuple back
     writeln(value.expand);
 }

 void main() {
     fun(1);
     fun(1, 2.2);
 }

 This prints out:
 1
 12.2

 But that's confusing, since the values are "glued" together when printed
 out. I really want to print out:
 1
 1 2.2

 Any ideas?
You can iterate on the values and create the corresponding string. Note that you must have a polymorphic function to map on a tuple's elements. void gun(T)(T value) { string result; foreach(index, Type; value.expand) { result ~= to!string(value.expand[i]) ~ " "; } writeln(result); } There will be a surnumerary " " at the very end, but I prefer to show the way to iterate on a tuple. value is a std.typecons.Tuple, but value.expand gives access to the raw (T...) tuple inside it.
 One other thing. I can use the .length property for value and parameter
 tuples, but only if I haven't built them myself with the call to
 std.typecons.tuple(). For example I can call writeln(T.length) in the fun()
 function (where the compiler automatically constructs a tuple), but not in
 the gun() function. So there seems to be compiler tuples and
 user-constructed tuples, which are not the same.
Yes, std.typecons.Tuple should have a length, it's trivial to add. It's http://d.puremagic.com/issues/show_bug.cgi?id=4381 And yes, there are 'raw', compiler-managed tuples which are lists of types and values 'glued' together. They are powerful, iterable, indexable, slicable, know their length, etc. They may even be assignable. Ah, in fact, they can hold anything that can be passed as a template argument, I guess. But, being a bunch of many different types, they cannot be returned by a function. That's C inheritance for you. They have no .init value neither, which I found too bad for generic code. http://d.puremagic.com/issues/show_bug.cgi?id=4536 So std.typecons.Tuple provides a way to wrap values inside a struct, while giving access to the types and values, and that can be returned by a function. It can also have named members, which can be quite handy when you return many things from a function, grouped inside a tuple. Tuple!("index", int, "sum", double) t = tuple(1,3.14); assert(t.index== 1); // t has .first and .second as members. assert(t.sum== 3.14);
 It's a bit of a shame that there isn't a chapter on tuples except this
 brief mention (I don't see it listed in the contents page). I guess I'll
 take a look at the implementation.
Andrei didn't want to talk too much about Phobos, as it was (ans still is!) in flux while he was writing this. D the language will not change much for some time, while the standard library is being actively transformed, if only to take into account the new features that were added for the past 6 months. btw, I like you posts, but do not always have the time to answer them. If among the numerous issues you posted there there is still one that bother you, do not hesitate to ask again for an answer. Philippe
Jul 30 2010
prev sibling next sibling parent Brad Roberts <braddr puremagic.com> writes:
On Fri, 30 Jul 2010, Andrej Mitrovic wrote:

 Speaking of which, I think there's a typo in Phobos docs:
 
 http://www.digitalmars.com/d/2.0/phobos/std_typecons.html
 
 Tuple!(int, int) point;
 // assign coordinates
 point.field[0] = 5;
 point.field[1] = 6;
 // read coordinates
 auto x = point.field[0];
 auto y = point.[1];
 
 The last one should be "auto y = point.field[1]"
Fix submitted to svn.
Jul 30 2010
prev sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Fri, Jul 30, 2010 at 20:20, Andrej Mitrovic
<andrej.mitrovich gmail.com>wrote:

 Hey, just tried out your code.

 In the first one there's a little typo (used 'i' instead of index), but
 that's ok. ;p
Ah yes, that was typed over the course of 2-3 hours, with my children jumping around :)
 It seems like I don't need to use the to!() template. This code seems to
 work fine:


 void gun(T)(T value) {
     foreach(Type; value.expand)
         write(Type, " ");

     writeln();
 }
If you just want to print them, yes. In that case btw, do not use 'Type' as a name, as you're directly iterating on the values. Sorry, my mistake: I'm so used to iterate on typetuples, where the iteration is done on a index-Type pair that I typed that without thinking.
 But regardless, at least I now know how to traverse through my own tuples,
 so thanks for that one.
As I said, value.expand is an instantiated typetuple, to it's iterable, indexed with [i], can be sliced with [i..j], it knows its length, etc. Very powerful stuff, very easy to use. None of the gobbledygook from C++. Note that .field and .expand are aliases, they are the same access to the tuple's fields, under a different name.
 In your second example:


 Tuple!("index", int, "sum", double) t = tuple(1,3.14);

 assert(t.index== 1); // t has .first and .second as members.
 assert(t.sum== 3.14);

 I get an error way deep in the library itself (this could call for a better
 error message, methinks):
 C:\DMD\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(320): Error: tuple
 index 2 exceeds 2

 I've reversed the order, put types before identifiers and this works fine
 then:

 Tuple!(int, "index", double, "sum") t = tuple(1, 3.14);
Sorry also, I should have checked typecons docs.
 I'm not sure what you mean by "t has .first and .second as members", I
 can't seem to access those. Maybe you meant t.index and t.sum as members?
Hem, as I said, I typed the mail over a few hours, the first version of the tuple was Tuple!((int, "first", double, "second"), but I found first and second a bit bland, so I changed that to .index and .sum. Man, I made a mess of my explanation. I'm glad you still understood something :)
Jul 30 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On page 143 there's a new find() overloaded function which is more
specialized than the last generic function, but I'm still getting an
ambiguity error. Although the book doesn't state if that specific unittest
should work, so I'm left wondering :) Here's the code:

void main()
{
}

T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (is(typeof(longer[0 .. 1] == shorter) : bool))
{
    while (longer.length >= shorter.length) {
        if (longer[0 .. shorter.length] == shorter)
            break;
        longer = longer[1 .. $];
    }
    return longer;
}

int[] find(int[] longer, int[] shorter) { }

unittest {
    int[] ints1 = [1, 2, 3, 5, 2] ;
    int[] ints2 = [3, 5];
    auto test = find(ints1, ints2);
}

test.d(16): Error: function test.find conflicts with template
test.find(T1,T2) if (is(typeof(longer[0..1] == shorter) : bool)) at
test.d(5)


And on page 145 the last example is:

void main() { }

void transmogrify(uint) { }
void transmogrify(long) { }
void transmogrify(T)(T value) { }

unittest {
    transmogrify(42);
    transmogrify("hello");
    transmogrify(1.1);
}

test2.d(5): Error: template test2.transmogrify(T) conflicts with function
test2.transmogrify at test2.d(3)

On Wed, Jul 28, 2010 at 7:32 PM, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 Woops, I got confused. I was thinking about structs, not arrays.

 But yeah, in this case a float gets compared to a double and it seems to
 evaluate to false.


 On Wed, Jul 28, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.com>wrote:

 On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:
 IIRC arrays are value types and as such they are compared bit-by-bit
 to each other, right?
Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Page 149, the two shorter-hand ways of writing function literals are not
working for me:

void main() { }

T[] find(alias pred, T)(T[] input)
    if (is(typeof(pred(input[0])) == bool))
{
    for (; input.length > 0; input = input[1 .. $]) {
        if (pred(input[0]))
            break;
    }
    return input;
}

unittest {
    int[] a = [1, 2, 3, 4, -5, 3, -4];

    auto b = find!(function bool(int x) { return x < 0; })(a);
    //~ auto c = find!(function(x) { return x < 0; })(a);
    //~ auto d = find!((x) { return x < 0; })(a);
}

The first one works fine. But if I uncomment the second or third line I get
these errors:

higher_order_functions.d(17): Error: template
higher_order_functions.find(alias pred,T) if (is(typeof(pred(input[0])) ==
bool)) does not match any function template declaration

higher_order_functions.d(17): Error: template
higher_order_functions.find(alias pred,T) if (is(typeof(pred(input[0])) ==
bool)) cannot deduce template function from argument types
!(__funcliteral1)(int[])


On Wed, Jul 28, 2010 at 10:44 PM, Andrej Mitrovic <
andrej.mitrovich gmail.com> wrote:

 On page 143 there's a new find() overloaded function which is more
 specialized than the last generic function, but I'm still getting an
 ambiguity error. Although the book doesn't state if that specific unittest
 should work, so I'm left wondering :) Here's the code:

 void main()

 {
 }

 T1[] find(T1, T2)(T1[] longer, T2[] shorter)
     if (is(typeof(longer[0 .. 1] == shorter) : bool))
 {
     while (longer.length >= shorter.length) {
         if (longer[0 .. shorter.length] == shorter)
             break;
         longer = longer[1 .. $];
     }
     return longer;
 }

 int[] find(int[] longer, int[] shorter) { }

 unittest {
     int[] ints1 = [1, 2, 3, 5, 2] ;
     int[] ints2 = [3, 5];
     auto test = find(ints1, ints2);
 }

 test.d(16): Error: function test.find conflicts with template
 test.find(T1,T2) if (is(typeof(longer[0..1] == shorter) : bool)) at
 test.d(5)


 And on page 145 the last example is:

 void main() { }

 void transmogrify(uint) { }
 void transmogrify(long) { }
 void transmogrify(T)(T value) { }

 unittest {
     transmogrify(42);
     transmogrify("hello");
     transmogrify(1.1);
 }

 test2.d(5): Error: template test2.transmogrify(T) conflicts with function
 test2.transmogrify at test2.d(3)


 On Wed, Jul 28, 2010 at 7:32 PM, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 Woops, I got confused. I was thinking about structs, not arrays.

 But yeah, in this case a float gets compared to a double and it seems to
 evaluate to false.


 On Wed, Jul 28, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.com>wrote:

 On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:
 IIRC arrays are value types and as such they are compared bit-by-bit
 to each other, right?
Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
They do work if I specify the type before x, as in:
    auto c = find!(function(int x) { return x < 0; })(a);
    auto d = find!((int x) { return x < 0; })(a);


On Wed, Jul 28, 2010 at 11:01 PM, Andrej Mitrovic <
andrej.mitrovich gmail.com> wrote:

 Page 149, the two shorter-hand ways of writing function literals are not
 working for me:

 void main() { }

 T[] find(alias pred, T)(T[] input)
     if (is(typeof(pred(input[0])) == bool))
 {
     for (; input.length > 0; input = input[1 .. $]) {
         if (pred(input[0]))
             break;
     }
     return input;
 }

 unittest {
     int[] a = [1, 2, 3, 4, -5, 3, -4];

     auto b = find!(function bool(int x) { return x < 0; })(a);
     //~ auto c = find!(function(x) { return x < 0; })(a);
     //~ auto d = find!((x) { return x < 0; })(a);
 }

 The first one works fine. But if I uncomment the second or third line I get
 these errors:

 higher_order_functions.d(17): Error: template
 higher_order_functions.find(alias pred,T) if (is(typeof(pred(input[0])) ==
 bool)) does not match any function template declaration

 higher_order_functions.d(17): Error: template
 higher_order_functions.find(alias pred,T) if (is(typeof(pred(input[0])) ==
 bool)) cannot deduce template function from argument types
 !(__funcliteral1)(int[])



 On Wed, Jul 28, 2010 at 10:44 PM, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 On page 143 there's a new find() overloaded function which is more
 specialized than the last generic function, but I'm still getting an
 ambiguity error. Although the book doesn't state if that specific unittest
 should work, so I'm left wondering :) Here's the code:

 void main()

 {
 }

 T1[] find(T1, T2)(T1[] longer, T2[] shorter)
     if (is(typeof(longer[0 .. 1] == shorter) : bool))
 {
     while (longer.length >= shorter.length) {
         if (longer[0 .. shorter.length] == shorter)
             break;
         longer = longer[1 .. $];
     }
     return longer;
 }

 int[] find(int[] longer, int[] shorter) { }

 unittest {
     int[] ints1 = [1, 2, 3, 5, 2] ;
     int[] ints2 = [3, 5];
     auto test = find(ints1, ints2);
 }

 test.d(16): Error: function test.find conflicts with template
 test.find(T1,T2) if (is(typeof(longer[0..1] == shorter) : bool)) at
 test.d(5)


 And on page 145 the last example is:

 void main() { }

 void transmogrify(uint) { }
 void transmogrify(long) { }
 void transmogrify(T)(T value) { }

 unittest {
     transmogrify(42);
     transmogrify("hello");
     transmogrify(1.1);
 }

 test2.d(5): Error: template test2.transmogrify(T) conflicts with function
 test2.transmogrify at test2.d(3)


 On Wed, Jul 28, 2010 at 7:32 PM, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 Woops, I got confused. I was thinking about structs, not arrays.

 But yeah, in this case a float gets compared to a double and it seems to
 evaluate to false.


 On Wed, Jul 28, 2010 at 7:26 PM, Jonathan M Davis <jmdavisprog gmail.com
 wrote:
 On Wednesday, July 28, 2010 09:49:52 Andrej Mitrovic wrote:
 IIRC arrays are value types and as such they are compared bit-by-bit
 to each other, right?
Dynamic arrays are reference types (though static arrays are indeed value types), and they evaluate for equality by comparing each element in turn (though presumably, they compare their length first and don't bother with element comparisons if the lengths don't match). Look at section 4.1.5 on page 100 of TDPL. - Jonathan M Davis
Jul 28 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Okay take a look at this:

import std.stdio;

void main()
{
}

T[] find(T, E)(T[] haystack, E needle)
    if (is(typeof(haystack[0] != needle) == bool))
{
    while (haystack.length > 0 && haystack[0] != needle) {
        haystack = haystack[1 .. $];
    }
    return haystack;
}


T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (is(typeof(longer[0 .. 1] == shorter) : bool) && false)
{
    while (longer.length >= shorter.length) {
        if (longer[0 .. shorter.length] == shorter)
            break;
        longer = longer[1 .. $];
    }
    return longer;
}

unittest {
    string[] longer = ["one", "two"];
    string shorter = "two";
    find(longer, shorter);

    writeln(is(typeof(longer[0 .. 1] == shorter) : bool) && false); //
writes false
}

I'm tyring to make the code in the unittest use the first templated
function. But it will fail because DMD matches the call to find to the
second templated function. But it should never match the second template,
I've forced it's constraint to always evaluate to false.

The writeln() in the unittest has the copy of the constraint expression and
it does evaluate to false. If I comment out the second template, this code
will compile as DMD will match the first template.

I'm using this forced "&& false" expression because I was trying out all
sorts of type/value expressions but they never worked for me.

Anyways, I'd appreciate any help here. :)




On Wed, Jul 28, 2010 at 5:22 PM, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 These templates seem to be hard to get right. I guess with experience they
 get easier to write and comprehend. Anyways, thanks for the explanation.


 On Wed, Jul 28, 2010 at 4:44 PM, Philippe Sigaud <
 philippe.sigaud gmail.com> wrote:

 On Wed, Jul 28, 2010 at 05:50, Andrej Mitrovic <
 andrej.mitrovich gmail.com> wrote:

 I won't comment on the double/float issue, I do not anything about it.


 T[] find(T, E)(T[] haystack, E needle)
    if (is(typeof(haystack[0] != needle) == bool))

 T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (is(typeof(longer[0 .. 1] == shorter) : bool))

 Also, Andrei, you never explained the if(is()) signature of the second
 templated function. I hope to get some pointers on that. :)
I'll have a try at this one. As you may know, the is(typeof()) syntax is a way to try to compile an expression and see if it works. If it works, it has a type, given by typeof and is(Type) returns true. So the first one is really saying: "Hey compiler, may I compare an element of haystack (of type T) with an E In this case, longer is an array. So longer[0] would be an element, a T1. There is no chance that shorter will match as an element cannot be equal to an array ... except, of course, if longer is an array of T2[] (T1 == T2[]). Longer is a T2[][]. That may happen for arrays of strings, strings being arrays of chars. And I think that's the problem you have in your last unit test. Anyway, he takes longer[0..1] to get a slice, which is a dynamic array, a T1[] and hence comparable to a T2[]... most of the time. As for using ': bool' instead of '== bool' as in the first find(), I don't think there is any difference in this case. I understand T == U as 'T is the exact same type as U', whereas T : U is for me "T is a subtype of U". But then, a subtype of bool is pretty much constrained to be a bool. The T : U syntax is pretty much only used for classes : if(is(T : MyClass)) is saying : 'compile this only if T is a subclass of MyClass'. Philippe , except if longer is in fact
Jul 30 2010
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
First,  trying a copy-paste of your code gives me an error in object.di
(DMD 2.047 on Windows)

C:\dmd\windows\bin\..\..\src\druntime\import\object.di|487|Error:
incompatible types for ((a) != (a2[i])): 'string' and 'immutable(char)'|
||=== Build finished: 1 errors, 0 warnings ===|

And Code::Blocks jumps me to object.di:

bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
{
    if (a1.length != a2.length)
return false;
    foreach(i, a; a1)
    { if (a != a2[i]) // <-- line 487. Uh oh, trying to compare strings and
chars
    return false;
    }
    return true;
}

I had to modify it:

bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
if (is(typeof(T1.init != T2.init))) // Are T1 and T2 comparable? If not, do
not bother to compile
{
   if (a1.length != a2.length) return false;
    foreach(i, a; a1)
    { if (a != a2[i])
        return false;
    }
    return true;
}

Another possibility would have it return false, but in that case, array
comparisons always compile and we lose the ability to use them as test for
instantiating a template.

I guess _ArrayEq is some internal called by the constraint, while doing the
comparison. Saving object.di and recompiling everything allows the templates
constraints to function and gives me a call to the first find.

Without the '&& false', it still calls the first find... because it still
cannot instantiate the second template:

The first find can be instantiated with T = immutable(char)[] and E =
immutable(char)[], there is nothing stopping it: T and E are the same type,
so they are trivially comparable. "two" is a valid target value for ["one",
"two"].
The second find would have T1 = immutable(char)[] and T2 = immutable(char)
and those do not compare.


testing it, find("one two three", "two") correctly calls the second version
and returns "two three".

So, maybe you just found a bug in object.di._ArrayEq. I'll post on the main
list. Anyway, for me, it seems to work, if I understand correctly what
you're doing.

Btw, if you find yourself using the same constraint regularly, you can
abstract it away in another template that returns (er, becomes, rather) a
boolean:

template areComparable(T1, T2)
{
   enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous
template trick
}

And then:

T[] find(T, E)(T[] haystack, E needle)
    if (areComparable!(T, E))
{
}


T[] find(T, E)(T[] longer, E[] shorter)
    if (areComparable!(T, E))

{
}

Which makes for cleaner code. I renamed T1 and T2 to T and E, so showcase
the difference between the two.


Philippe
Jul 30 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On Sat, Jul 31, 2010 at 12:04 AM, Philippe Sigaud <philippe.sigaud gmail.com
 wrote:
 First,  trying a copy-paste of your code gives me an error in object.di
 (DMD 2.047 on Windows)
Yes, I get the same error.
 C:\dmd\windows\bin\..\..\src\druntime\import\object.di|487|Error:
 incompatible types for ((a) != (a2[i])): 'string' and 'immutable(char)'|
 ||=== Build finished: 1 errors, 0 warnings ===|

 And Code::Blocks jumps me to object.di:

 bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
 {
     if (a1.length != a2.length)
  return false;
     foreach(i, a; a1)
     { if (a != a2[i]) // <-- line 487. Uh oh, trying to compare strings
 and chars
     return false;
     }
     return true;
 }

 I had to modify it:

 bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
  if (is(typeof(T1.init != T2.init))) // Are T1 and T2 comparable? If not,
 do not bother to compile
 {
    if (a1.length != a2.length) return false;
     foreach(i, a; a1)
     { if (a != a2[i])
         return false;
     }
     return true;
 }

 Another possibility would have it return false, but in that case, array
 comparisons always compile and we lose the ability to use them as test for
 instantiating a template.

 I guess _ArrayEq is some internal called by the constraint, while doing the
 comparison. Saving object.di and recompiling everything allows the templates
 constraints to function and gives me a call to the first find.

 Without the '&& false', it still calls the first find... because it still
 cannot instantiate the second template:

 The first find can be instantiated with T = immutable(char)[] and E =
 immutable(char)[], there is nothing stopping it: T and E are the same type,
 so they are trivially comparable. "two" is a valid target value for ["one",
 "two"].
 The second find would have T1 = immutable(char)[] and T2 = immutable(char)
 and those do not compare.


 testing it, find("one two three", "two") correctly calls the second version
 and returns "two three".
So, maybe you just found a bug in object.di._ArrayEq. I'll post on the main
 list. Anyway, for me, it seems to work, if I understand correctly what
 you're doing.
What I am trying to do is to get the code sample from TDPL working. I tried to constraint the second template so it never gets called with: assert(find(s, "two") == ["two"]); because this was in the unittest right under the first template, and I assumed it should work only with the first template. On TDPL page 143 it states that there is no ambiguity between the two templates, so this entire following code should work (could you please try this out with your new object.di if you have any time?). I hope Andrei won't mind for pasting this here: import std.stdio; void main() { } T[] find(T, E)(T[] haystack, E needle) if (is(typeof(haystack[0] != needle) == bool)) { while (haystack.length > 0 && haystack[0] != needle) { haystack = haystack[1 .. $]; } return haystack; } unittest { double[] d = [1.5, 2.4]; assert(find(d, 1.0) == null); assert(find(d, 1.5) == d); string[] s = ["one", "two"]; assert(find(s, "two") == ["two"]); } T1[] find(T1, T2)(T1[] longer, T2[] shorter) if (is(typeof(longer[0 .. 1] == shorter) : bool)) { while (longer.length >= shorter.length) { if (longer[0 .. shorter.length] == shorter) break; longer = longer[1 .. $]; } return longer; } unittest { double[] d1 = [6.0, 1.5, 2.4, 3]; float[] d2 = [1.5, 2.4]; assert(find(d1, d2) == d1[1 .. $]); }
 Btw, if you find yourself using the same constraint regularly, you can
 abstract it away in another template that returns (er, becomes, rather) a
 boolean:

 template areComparable(T1, T2)
 {
    enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous
 template trick
 }

 And then:

 T[] find(T, E)(T[] haystack, E needle)
     if (areComparable!(T, E))
 {
 }


 T[] find(T, E)(T[] longer, E[] shorter)
     if (areComparable!(T, E))

 {
 }

 Which makes for cleaner code. I renamed T1 and T2 to T and E, so showcase
 the difference between the two.


 Philippe
I was actually thinking about ways of simplifying the constraint definitions with some helper functions. That's a pretty cool trick, thanks!
Jul 30 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I think there really is some bug here. I tried to use your template helper
function, and it seems the order in which I put two constraints in a
template signature changes the way dmd compiles the code. Consider this:

import std.stdio;

void main()
{
}

template areComparable(T1, T2)
{
   enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous
template trick
}

T[] find(T, E)(T[] haystack, E needle)
    if (is(typeof(haystack[0] != needle) == bool) &&
    areComparable!(T, E))
{
    while (haystack.length > 0 && haystack[0] != needle) {
        haystack = haystack[1 .. $];
    }
    return haystack;
}

T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (is(typeof(longer[0 .. 1] == shorter) : bool) && (areComparable!(T1,
T2)))

{
    while (longer.length >= shorter.length) {
        if (longer[0 .. shorter.length] == shorter)
            break;
        longer = longer[1 .. $];
    }
    return longer;
}

unittest {
    double[] d = [1.5, 2.4];
    assert(find(d, 1.0) == null);
    assert(find(d, 1.5) == d);
    string[] s = ["one", "two"];
    assert(find(s, "two") == ["two"]);
}

This will give out the error "incompatible types for comparing of string and
immutable(char)". But if I reverse the order of the constraints in the
second template, like so:

import std.stdio;

void main()
{
}

template areComparable(T1, T2)
{
   enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous
template trick
}

T[] find(T, E)(T[] haystack, E needle)
    if (is(typeof(haystack[0] != needle) == bool) &&
    areComparable!(T, E))
{
    while (haystack.length > 0 && haystack[0] != needle) {
        haystack = haystack[1 .. $];
    }
    return haystack;
}


T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (areComparable!(T1, T2) && is(typeof(longer[0 .. 1] == shorter) :
bool))
{
    while (longer.length >= shorter.length) {
        if (longer[0 .. shorter.length] == shorter)
            break;
        longer = longer[1 .. $];
    }
    return longer;
}

unittest {
    double[] d = [1.5, 2.4];
    assert(find(d, 1.0) == null);
    assert(find(d, 1.5) == d);
    string[] s = ["one", "two"];
    assert(find(s, "two") == ["two"]);
}

Then this happily compiles. All of the asserts will call the first templated
function.


On Sat, Jul 31, 2010 at 12:04 AM, Philippe Sigaud <philippe.sigaud gmail.com
 wrote:
 First,  trying a copy-paste of your code gives me an error in object.di
 (DMD 2.047 on Windows)

 C:\dmd\windows\bin\..\..\src\druntime\import\object.di|487|Error:
 incompatible types for ((a) != (a2[i])): 'string' and 'immutable(char)'|
 ||=== Build finished: 1 errors, 0 warnings ===|

 And Code::Blocks jumps me to object.di:

 bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
 {
     if (a1.length != a2.length)
  return false;
     foreach(i, a; a1)
     { if (a != a2[i]) // <-- line 487. Uh oh, trying to compare strings
 and chars
     return false;
     }
     return true;
 }

 I had to modify it:

 bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
  if (is(typeof(T1.init != T2.init))) // Are T1 and T2 comparable? If not,
 do not bother to compile
 {
    if (a1.length != a2.length) return false;
     foreach(i, a; a1)
     { if (a != a2[i])
         return false;
     }
     return true;
 }

 Another possibility would have it return false, but in that case, array
 comparisons always compile and we lose the ability to use them as test for
 instantiating a template.

 I guess _ArrayEq is some internal called by the constraint, while doing the
 comparison. Saving object.di and recompiling everything allows the templates
 constraints to function and gives me a call to the first find.

 Without the '&& false', it still calls the first find... because it still
 cannot instantiate the second template:

 The first find can be instantiated with T = immutable(char)[] and E =
 immutable(char)[], there is nothing stopping it: T and E are the same type,
 so they are trivially comparable. "two" is a valid target value for ["one",
 "two"].
 The second find would have T1 = immutable(char)[] and T2 = immutable(char)
 and those do not compare.


 testing it, find("one two three", "two") correctly calls the second version
 and returns "two three".

 So, maybe you just found a bug in object.di._ArrayEq. I'll post on the main
 list. Anyway, for me, it seems to work, if I understand correctly what
 you're doing.

 Btw, if you find yourself using the same constraint regularly, you can
 abstract it away in another template that returns (er, becomes, rather) a
 boolean:

 template areComparable(T1, T2)
 {
    enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous
 template trick
 }

 And then:

 T[] find(T, E)(T[] haystack, E needle)
     if (areComparable!(T, E))
 {
 }


 T[] find(T, E)(T[] longer, E[] shorter)
     if (areComparable!(T, E))

 {
 }

 Which makes for cleaner code. I renamed T1 and T2 to T and E, so showcase
 the difference between the two.


 Philippe
Jul 31 2010
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sat, Jul 31, 2010 at 17:24, Andrej Mitrovic
<andrej.mitrovich gmail.com>wrote:

 I think there really is some bug here. I tried to use your template helper
 function, and it seems the order in which I put two constraints in a
 template signature changes the way dmd compiles the code. Consider this:
 T[] find(T, E)(T[] haystack, E needle)
     if (is(typeof(haystack[0] != needle) == bool) &&
     areComparable!(T, E))

 ...
 T1[] find(T1, T2)(T1[] longer, T2[] shorter)
     if (is(typeof(longer[0 .. 1] == shorter) : bool) && (areComparable!(T1,
 T2)))
 This will give out the error "incompatible types for comparing of string
 and immutable(char)".
That's because the first part of the constraint is tested, to allow for short-circuiting the second part. So the bad array comparison triggers the problem in object._EqArray. You should always test for the most general constraint first, I guess. In this particular case, use areComparable!(A,B) as the first argument to && in both templates.
 But if I reverse the order of the constraints in the second template, like
 so:

 T1[] find(T1, T2)(T1[] longer, T2[] shorter)
     if (areComparable!(T1, T2) && is(typeof(longer[0 .. 1] == shorter) :
 bool))
 Then this happily compiles.
Well, yes. The second template cannot be instantiated: T1 is a string, T2 is a character. So isComparable evaluates to 'false' and the constraints short-circuits to 'false'.
 All of the asserts will call the first templated function.
Because you are looking for an element in a sequence of element in all three cases. That's what the first version of find() do. If you test: assert(find(d, [1.5]); // [1.5] is a one-element array. assert(find(d, [1.5, 2.4]); // finding d in d. assert(find("one two three", "two"); it will activate the second version for all these new asserts: you are looking for a sequence of elements in a sequence of elements, which is what the second find does. Philippe
Aug 01 2010
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sat, Jul 31, 2010 at 00:58, Andrej Mitrovic
<andrej.mitrovich gmail.com>wrote:

 could you please try this out with your new object.di if you have any
 time?). I hope Andrei won't mind for pasting this here:


 (snip)
 unittest {
     double[] d1 = [6.0, 1.5, 2.4, 3];
     float[] d2 = [1.5, 2.4];
     assert(find(d1, d2) == d1[1 .. $]); // unit test failure on line 40.
 }
The assert fails, because find(d1, d2) does not returns [1.5, 2.4, 3] in my case. That's because floating points types are notoriously difficult to test for equality: double d = 2.4; float f = 2.4; writeln(d == f); // false! 2.4 do not have the same representation as a float or as a double. So I guess there must have been some pb with the testing Andrei has done on the examples, because this particular example do not work. To test floating point values, use std.math.approxEqual. For a function like find() you can imagine either using approxEqual as the comparator when you detect T/E, T1/T2 to be floating point values, or defer to the user to give you a valid comparator, as an alias: import std.functional: binaryFun; // template transforming strings like "a ==b" into functions. T1[] find(alias comparator = "a == b", T1, T2)(T1[] longer, T2[] shorter) if (is(typeof( binaryFun!comparator(longer[0 .. 1], shorter)) : bool)) { while (longer.length >= shorter.length) { if (binaryFun!comparator(longer[0 .. shorter.length], shorter)) break; longer = longer[1 .. $]; } return longer; } usage: void main() { double[] d = [1.0, 1.5, 2.4, 3.14]; float[] f = [1.5, 2.4]; writeln(find(d, f)); // use a == b as a comparator, so returns 3.14, did not see 1.5 float and 1.5 double as equal writeln(find!approxEqual(d, f)); // [1.5, 2.4, 3.14] } btw, the behavior is not correct: find(d,f) should return an empty array, not [3.14]... I was actually thinking about ways of simplifying the constraint definitions
 with some helper functions. That's a pretty cool trick, thanks!
It's used throughout Phobos, particularly in the template-heavy modules, like std.algorithm or std.range. Philippe
Aug 01 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On Sun, Aug 1, 2010 at 11:15 PM, Philippe Sigaud
<philippe.sigaud gmail.com>wrote:

 On Sat, Jul 31, 2010 at 17:24, Andrej Mitrovic <andrej.mitrovich gmail.com
 wrote:
 I think there really is some bug here. I tried to use your template helper
 function, and it seems the order in which I put two constraints in a
 template signature changes the way dmd compiles the code. Consider this:
 T[] find(T, E)(T[] haystack, E needle)
     if (is(typeof(haystack[0] != needle) == bool) &&
     areComparable!(T, E))

 ...
 T1[] find(T1, T2)(T1[] longer, T2[] shorter)
     if (is(typeof(longer[0 .. 1] == shorter) : bool) &&
 (areComparable!(T1, T2)))
 This will give out the error "incompatible types for comparing of string
 and immutable(char)".
That's because the first part of the constraint is tested, to allow for short-circuiting the second part. So the bad array comparison triggers the problem in object._EqArray. You should always test for the most general constraint first, I guess. In this particular case, use areComparable!(A,B) as the first argument to && in both templates.
Let me see if I'm getting this right. false && true will short circuit to false (this is normal). true && false will short circuit to true? How can that be? It's not a logical OR, its an AND, and as far as I know both expressions need to be True to evaluate to True. Or is it different for template constraints? Thanks again for the std.math.approxEqual and binaryFun tips.
Aug 01 2010
prev sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Mon, Aug 2, 2010 at 00:37, Andrej Mitrovic <andrej.mitrovich gmail.com>wrote:

 That's because the first part of the constraint is tested, to allow for
 short-circuiting the second part. So the bad array comparison triggers the
 problem in object._EqArray.

 You should always test for the most general constraint first, I guess. In
 this particular case, use areComparable!(A,B) as the first argument to && in
 both templates.
Let me see if I'm getting this right. false && true will short circuit to false (this is normal). true && false will short circuit to true? How can that be? It's not a logical OR, its an AND, and as far as I know both expressions need to be True to evaluate to True. Or is it different for template constraints?
I was answering you about the "incompatible types" error. The first part of the template, 'is(typeof(longer[0 .. 1] == shorter) : bool', triggers the error and that stops the compilation process. I agree that, if longer[0..1] == shorter is an error, then we could have is(typeof()) result in 'true'. But here what happens is that everything stops. I'll post about this on the main list, see if it's normal behavior or not and add a bug report. Philippe
Aug 01 2010