Re: Generic collection/element function signatures in D2 versus D1
On 9/11/2010 9:32 PM, Nick Sabalausky wrote: > "Jacob Carlborg" wrote in message > news:i5t61q$2j7...@digitalmars.com... >> >> If you're not going to modify the content of the array I think this will >> work: >> >> void foo (T) (const(T)[] collection, T elem) {} >> >> This will allow both mutable, immutable and const arrays. But it will not >> let you modify the array like this: >> >> collection[3] = 'a'; >> > > On DMD 2.048, This isn't working for me: > > - > void foo(T)(const(T)[] coll, T elem) > { > } > > void main() > { > string x = "hello"; > foo(x, x[1]); > } > > - > > Result: > - > testStringAndChar.d(8): Error: template testStringAndChar.foo(T) does not > match any function template declaration > testStringAndChar.d(8): Error: template testStringAndChar.foo(T) cannot > deduce template function from argument types !()(string,immutable(char)) > - > > I figured that was just because 'const(immutable(T))[]' doesn't make much > sence, so I tried this and got the exact same error messages: > > - > import std.traits; > > void foo(T)(const(Unqual!T)[] coll, T elem) > { > } > > void main() > { > string x = "hello"; > foo(x, x[1]); > } > - > > It seems to be a problem with IFTI, because this does work with both > versions: > foo!char(x, x[1]); > > Kind of a pain. > http://d.puremagic.com/issues/show_bug.cgi?id=2594
Re: Generic collection/element function signatures in D2 versus D1
"Jacob Carlborg" wrote in message news:i5t61q$2j7...@digitalmars.com... > > If you're not going to modify the content of the array I think this will > work: > > void foo (T) (const(T)[] collection, T elem) {} > > This will allow both mutable, immutable and const arrays. But it will not > let you modify the array like this: > > collection[3] = 'a'; > On DMD 2.048, This isn't working for me: - void foo(T)(const(T)[] coll, T elem) { } void main() { string x = "hello"; foo(x, x[1]); } - Result: - testStringAndChar.d(8): Error: template testStringAndChar.foo(T) does not match any function template declaration testStringAndChar.d(8): Error: template testStringAndChar.foo(T) cannot deduce template function from argument types !()(string,immutable(char)) - I figured that was just because 'const(immutable(T))[]' doesn't make much sence, so I tried this and got the exact same error messages: - import std.traits; void foo(T)(const(Unqual!T)[] coll, T elem) { } void main() { string x = "hello"; foo(x, x[1]); } - It seems to be a problem with IFTI, because this does work with both versions: foo!char(x, x[1]); Kind of a pain.
Re: Generic collection/element function signatures in D2 versus D1
On Wed, 08 Sep 2010 12:38:44 -0400, Pelle wrote: On 09/08/2010 02:24 PM, Steven Schveighoffer wrote: On Tue, 07 Sep 2010 14:06:58 -0400, Steven Schveighoffer wrote: On Tue, 07 Sep 2010 11:37:20 -0400, Pelle wrote: On 09/07/2010 04:33 PM, Steven Schveighoffer wrote: Yes, a valid return. Your function should be: void foo(void delegate(const(C) f) const It helps to understand that inout/const/immutable has NOTHING to do with code generation, it only has to do with limiting what compiles. For this reason, an inout function is compiled once, and works on all three constancies (4 if you have a nested inout function). For the entire function any inout variable is treated as a non-changeable value, just like const. Then when you return, it's converted at the call site back to the constancy with which it was called. If the return value is void, then there's nothing to convert, and no reason to use inout over const. I'll repeat -- there is no benefit to inout if you are not returning anything. -Steve That's not an equivalent function signature. Or maybe it is, but look at this (sorry it's so long): class C { int x; this(int y) { x = y; } inout(int*) foo() inout { return &x; } void bar(void delegate(int*) f) { f(&x); } void bar(void delegate(const(int*)) f) const { f(&x); } void bar(void delegate(immutable(int*)) f) immutable { f(&x); } } void main() { immutable(int)* wah; void wahwah(immutable(int*) x) { wah = x; } auto c = new immutable(C)(10); wahwah(c.foo); // why is this privilegied with inout c.bar(&wahwah); // and this not? writeln(*wah); } Can't use void delegate(const(int*)) there. Thanks for clarifying, I didn't quite understand the usage before. This is a limitation of inout's design. Technically inout requires a single inout output, and can have multiple inout inputs. Your example matches that description, so in theory it's possible. I realized last night that this won't work. Simple reason -- during an inout function, the function promises to treat the arguments as if they were const. If your class instance was mutable, this would not be true, e.g.: class C { int x; void bar(void delegate(inout(int)* f) inout { f(&x); } } void main() { auto c = new C; void dg(int *f) {*f = 10;} c.bar(&dg); // modifies c, even though the function is marked as inout } -Steve Well, inout was conceived as a counter to where you need to write the exact same function for every type of constness, was it not? :-) Not exactly, it was conceived as a solution for the case where you would normally write a const function, but you don't want it to alter your const contract with the object in question. Basically, you want a function that promises not to alter the parameters, but gives you back one of your parameters or a portion of one of them in the same constancy you pass in. The classic example is a property getter. If you are not returning a portion of the input, then you can use const instead. -Steve
Re: Generic collection/element function signatures in D2 versus D1
On 09/08/2010 02:24 PM, Steven Schveighoffer wrote: On Tue, 07 Sep 2010 14:06:58 -0400, Steven Schveighoffer wrote: On Tue, 07 Sep 2010 11:37:20 -0400, Pelle wrote: On 09/07/2010 04:33 PM, Steven Schveighoffer wrote: Yes, a valid return. Your function should be: void foo(void delegate(const(C) f) const It helps to understand that inout/const/immutable has NOTHING to do with code generation, it only has to do with limiting what compiles. For this reason, an inout function is compiled once, and works on all three constancies (4 if you have a nested inout function). For the entire function any inout variable is treated as a non-changeable value, just like const. Then when you return, it's converted at the call site back to the constancy with which it was called. If the return value is void, then there's nothing to convert, and no reason to use inout over const. I'll repeat -- there is no benefit to inout if you are not returning anything. -Steve That's not an equivalent function signature. Or maybe it is, but look at this (sorry it's so long): class C { int x; this(int y) { x = y; } inout(int*) foo() inout { return &x; } void bar(void delegate(int*) f) { f(&x); } void bar(void delegate(const(int*)) f) const { f(&x); } void bar(void delegate(immutable(int*)) f) immutable { f(&x); } } void main() { immutable(int)* wah; void wahwah(immutable(int*) x) { wah = x; } auto c = new immutable(C)(10); wahwah(c.foo); // why is this privilegied with inout c.bar(&wahwah); // and this not? writeln(*wah); } Can't use void delegate(const(int*)) there. Thanks for clarifying, I didn't quite understand the usage before. This is a limitation of inout's design. Technically inout requires a single inout output, and can have multiple inout inputs. Your example matches that description, so in theory it's possible. I realized last night that this won't work. Simple reason -- during an inout function, the function promises to treat the arguments as if they were const. If your class instance was mutable, this would not be true, e.g.: class C { int x; void bar(void delegate(inout(int)* f) inout { f(&x); } } void main() { auto c = new C; void dg(int *f) {*f = 10;} c.bar(&dg); // modifies c, even though the function is marked as inout } -Steve Well, inout was conceived as a counter to where you need to write the exact same function for every type of constness, was it not? :-)
Re: Generic collection/element function signatures in D2 versus D1
On Tue, 07 Sep 2010 14:06:58 -0400, Steven Schveighoffer wrote: On Tue, 07 Sep 2010 11:37:20 -0400, Pelle wrote: On 09/07/2010 04:33 PM, Steven Schveighoffer wrote: Yes, a valid return. Your function should be: void foo(void delegate(const(C) f) const It helps to understand that inout/const/immutable has NOTHING to do with code generation, it only has to do with limiting what compiles. For this reason, an inout function is compiled once, and works on all three constancies (4 if you have a nested inout function). For the entire function any inout variable is treated as a non-changeable value, just like const. Then when you return, it's converted at the call site back to the constancy with which it was called. If the return value is void, then there's nothing to convert, and no reason to use inout over const. I'll repeat -- there is no benefit to inout if you are not returning anything. -Steve That's not an equivalent function signature. Or maybe it is, but look at this (sorry it's so long): class C { int x; this(int y) { x = y; } inout(int*) foo() inout { return &x; } void bar(void delegate(int*) f) { f(&x); } void bar(void delegate(const(int*)) f) const { f(&x); } void bar(void delegate(immutable(int*)) f) immutable { f(&x); } } void main() { immutable(int)* wah; void wahwah(immutable(int*) x) { wah = x; } auto c = new immutable(C)(10); wahwah(c.foo); // why is this privilegied with inout c.bar(&wahwah); // and this not? writeln(*wah); } Can't use void delegate(const(int*)) there. Thanks for clarifying, I didn't quite understand the usage before. This is a limitation of inout's design. Technically inout requires a single inout output, and can have multiple inout inputs. Your example matches that description, so in theory it's possible. I realized last night that this won't work. Simple reason -- during an inout function, the function promises to treat the arguments as if they were const. If your class instance was mutable, this would not be true, e.g.: class C { int x; void bar(void delegate(inout(int)* f) inout { f(&x); } } void main() { auto c = new C; void dg(int *f) {*f = 10;} c.bar(&dg); // modifies c, even though the function is marked as inout } -Steve
Re: Generic collection/element function signatures in D2 versus D1
On 2010-09-07 17:37, Pelle wrote: On 09/07/2010 04:33 PM, Steven Schveighoffer wrote: Yes, a valid return. Your function should be: void foo(void delegate(const(C) f) const It helps to understand that inout/const/immutable has NOTHING to do with code generation, it only has to do with limiting what compiles. For this reason, an inout function is compiled once, and works on all three constancies (4 if you have a nested inout function). For the entire function any inout variable is treated as a non-changeable value, just like const. Then when you return, it's converted at the call site back to the constancy with which it was called. If the return value is void, then there's nothing to convert, and no reason to use inout over const. I'll repeat -- there is no benefit to inout if you are not returning anything. -Steve That's not an equivalent function signature. Or maybe it is, but look at this (sorry it's so long): class C { int x; this(int y) { x = y; } inout(int*) foo() inout { return &x; } void bar(void delegate(int*) f) { f(&x); } void bar(void delegate(const(int*)) f) const { f(&x); } void bar(void delegate(immutable(int*)) f) immutable { f(&x); } } void main() { immutable(int)* wah; void wahwah(immutable(int*) x) { wah = x; } auto c = new immutable(C)(10); wahwah(c.foo); // why is this privilegied with inout c.bar(&wahwah); // and this not? writeln(*wah); } Can't use void delegate(const(int*)) there. That won't work, you can't have inout on return of a function without having inout for a least one parameter. The compiler can't know what to resolve inout to, mutable, immutable or const. -- /Jacob Carlborg
Re: Generic collection/element function signatures in D2 versus D1
On Tue, 07 Sep 2010 11:37:20 -0400, Pelle wrote: On 09/07/2010 04:33 PM, Steven Schveighoffer wrote: Yes, a valid return. Your function should be: void foo(void delegate(const(C) f) const It helps to understand that inout/const/immutable has NOTHING to do with code generation, it only has to do with limiting what compiles. For this reason, an inout function is compiled once, and works on all three constancies (4 if you have a nested inout function). For the entire function any inout variable is treated as a non-changeable value, just like const. Then when you return, it's converted at the call site back to the constancy with which it was called. If the return value is void, then there's nothing to convert, and no reason to use inout over const. I'll repeat -- there is no benefit to inout if you are not returning anything. -Steve That's not an equivalent function signature. Or maybe it is, but look at this (sorry it's so long): class C { int x; this(int y) { x = y; } inout(int*) foo() inout { return &x; } void bar(void delegate(int*) f) { f(&x); } void bar(void delegate(const(int*)) f) const { f(&x); } void bar(void delegate(immutable(int*)) f) immutable { f(&x); } } void main() { immutable(int)* wah; void wahwah(immutable(int*) x) { wah = x; } auto c = new immutable(C)(10); wahwah(c.foo); // why is this privilegied with inout c.bar(&wahwah); // and this not? writeln(*wah); } Can't use void delegate(const(int*)) there. Thanks for clarifying, I didn't quite understand the usage before. This is a limitation of inout's design. Technically inout requires a single inout output, and can have multiple inout inputs. Your example matches that description, so in theory it's possible. But to simplify things, a function's only inout output must be on the return value. So things like void fn(inout int* x, out inout(int) y) don't qualify either. IMO there is a whole host of implicit delegate casting that should be possible, but isn't, including delegate contravariance. However, let's get inout working as it's currently designed before we go relaxing the requirements. I'm not a compiler/language designer, so I'm unsure if what you want has any pitfalls. -Steve
Re: Generic collection/element function signatures in D2 versus D1
On 09/07/2010 04:33 PM, Steven Schveighoffer wrote: Yes, a valid return. Your function should be: void foo(void delegate(const(C) f) const It helps to understand that inout/const/immutable has NOTHING to do with code generation, it only has to do with limiting what compiles. For this reason, an inout function is compiled once, and works on all three constancies (4 if you have a nested inout function). For the entire function any inout variable is treated as a non-changeable value, just like const. Then when you return, it's converted at the call site back to the constancy with which it was called. If the return value is void, then there's nothing to convert, and no reason to use inout over const. I'll repeat -- there is no benefit to inout if you are not returning anything. -Steve That's not an equivalent function signature. Or maybe it is, but look at this (sorry it's so long): class C { int x; this(int y) { x = y; } inout(int*) foo() inout { return &x; } void bar(void delegate(int*) f) { f(&x); } void bar(void delegate(const(int*)) f) const { f(&x); } void bar(void delegate(immutable(int*)) f) immutable { f(&x); } } void main() { immutable(int)* wah; void wahwah(immutable(int*) x) { wah = x; } auto c = new immutable(C)(10); wahwah(c.foo); // why is this privilegied with inout c.bar(&wahwah); // and this not? writeln(*wah); } Can't use void delegate(const(int*)) there.
Re: Generic collection/element function signatures in D2 versus D1
On Tue, 07 Sep 2010 09:28:18 -0400, Pelle wrote: On 09/07/2010 03:15 PM, Steven Schveighoffer wrote: On Tue, 07 Sep 2010 08:56:15 -0400, Jacob Carlborg wrote: On 2010-09-07 14:49, Steven Schveighoffer wrote: On Sun, 05 Sep 2010 09:40:59 -0400, BLS wrote: On 05/09/2010 02:16, Jonathan M Davis wrote: void foo(T)(T[] collection, T elem) { // Blah, whatever } I am curious, how this will look and feel once inout is working ? inout void foo(T)(inout(T)[] collection, inout T elem) { // Blah, whatever} } inout void doesn't make any sense. You can't have a const void or immutable void. Now, if foo is a member function, then inout applies to the "this" pointer, but even then, you need a return type other than void for inout to be used. -Steve inout is only used when you want to return the same constness (mutable, const, immutable) as you passed in to the function. If you don't want that, or don't want to return anything then const(T)[] is what you want. It will accept mutable, const and immutable. Yes, exactly. This is why inout functions cannot return void. -Steve Hmm. class C { void foo(void delegate(inout(C)) f) inout { f(this); } } Am I missing something? Yes, a valid return. Your function should be: void foo(void delegate(const(C) f) const It helps to understand that inout/const/immutable has NOTHING to do with code generation, it only has to do with limiting what compiles. For this reason, an inout function is compiled once, and works on all three constancies (4 if you have a nested inout function). For the entire function any inout variable is treated as a non-changeable value, just like const. Then when you return, it's converted at the call site back to the constancy with which it was called. If the return value is void, then there's nothing to convert, and no reason to use inout over const. I'll repeat -- there is no benefit to inout if you are not returning anything. -Steve
Re: Generic collection/element function signatures in D2 versus D1
On 09/07/2010 03:15 PM, Steven Schveighoffer wrote: On Tue, 07 Sep 2010 08:56:15 -0400, Jacob Carlborg wrote: On 2010-09-07 14:49, Steven Schveighoffer wrote: On Sun, 05 Sep 2010 09:40:59 -0400, BLS wrote: On 05/09/2010 02:16, Jonathan M Davis wrote: void foo(T)(T[] collection, T elem) { // Blah, whatever } I am curious, how this will look and feel once inout is working ? inout void foo(T)(inout(T)[] collection, inout T elem) { // Blah, whatever} } inout void doesn't make any sense. You can't have a const void or immutable void. Now, if foo is a member function, then inout applies to the "this" pointer, but even then, you need a return type other than void for inout to be used. -Steve inout is only used when you want to return the same constness (mutable, const, immutable) as you passed in to the function. If you don't want that, or don't want to return anything then const(T)[] is what you want. It will accept mutable, const and immutable. Yes, exactly. This is why inout functions cannot return void. -Steve Hmm. class C { void foo(void delegate(inout(C)) f) inout { f(this); } } Am I missing something?
Re: Generic collection/element function signatures in D2 versus D1
On Tue, 07 Sep 2010 08:56:15 -0400, Jacob Carlborg wrote: On 2010-09-07 14:49, Steven Schveighoffer wrote: On Sun, 05 Sep 2010 09:40:59 -0400, BLS wrote: On 05/09/2010 02:16, Jonathan M Davis wrote: void foo(T)(T[] collection, T elem) { // Blah, whatever } I am curious, how this will look and feel once inout is working ? inout void foo(T)(inout(T)[] collection, inout T elem) { // Blah, whatever} } inout void doesn't make any sense. You can't have a const void or immutable void. Now, if foo is a member function, then inout applies to the "this" pointer, but even then, you need a return type other than void for inout to be used. -Steve inout is only used when you want to return the same constness (mutable, const, immutable) as you passed in to the function. If you don't want that, or don't want to return anything then const(T)[] is what you want. It will accept mutable, const and immutable. Yes, exactly. This is why inout functions cannot return void. -Steve
Re: Generic collection/element function signatures in D2 versus D1
On 2010-09-07 14:49, Steven Schveighoffer wrote: On Sun, 05 Sep 2010 09:40:59 -0400, BLS wrote: On 05/09/2010 02:16, Jonathan M Davis wrote: void foo(T)(T[] collection, T elem) { // Blah, whatever } I am curious, how this will look and feel once inout is working ? inout void foo(T)(inout(T)[] collection, inout T elem) { // Blah, whatever} } inout void doesn't make any sense. You can't have a const void or immutable void. Now, if foo is a member function, then inout applies to the "this" pointer, but even then, you need a return type other than void for inout to be used. -Steve inout is only used when you want to return the same constness (mutable, const, immutable) as you passed in to the function. If you don't want that, or don't want to return anything then const(T)[] is what you want. It will accept mutable, const and immutable. -- /Jacob Carlborg
Re: Generic collection/element function signatures in D2 versus D1
On Sun, 05 Sep 2010 09:40:59 -0400, BLS wrote: On 05/09/2010 02:16, Jonathan M Davis wrote: void foo(T)(T[] collection, T elem) { // Blah, whatever } I am curious, how this will look and feel once inout is working ? inout void foo(T)(inout(T)[] collection, inout T elem) { // Blah, whatever} } inout void doesn't make any sense. You can't have a const void or immutable void. Now, if foo is a member function, then inout applies to the "this" pointer, but even then, you need a return type other than void for inout to be used. -Steve
Re: Generic collection/element function signatures in D2 versus D1
On 05/09/2010 02:16, Jonathan M Davis wrote: void foo(T)(T[] collection, T elem) { // Blah, whatever } I am curious, how this will look and feel once inout is working ? inout void foo(T)(inout(T)[] collection, inout T elem) { // Blah, whatever} }
Re: Generic collection/element function signatures in D2 versus D1
On Saturday 04 September 2010 00:57:48 Nick Sabalausky wrote: > In D1 I did this sort of thing a fair amount: > > void foo(T)(T[] collection, T elem) > { > // Blah, whatever > } > > Worked for any of the string types, worked for any array, or anything with > the appropriate opIndexes, and for all I know there may be some improvement > that could still be made. But of course, in D2 strings have that extra > immutable part that mucks up the above for strings (and then there's > ranges), so: Is there a typical generally-best way in D2 to declare a > function signature for operating on collections and elements? I know it > would involve using the standard range interfaces in the body and choosing > the most restrictive range type that gets the job done, and I'm fine with > all that, but is there a good example of a typical "best-practice" > generic-function signature in D2? > > --- > Not sent from an iPhone. Okay, here's a shot at it: import std.algorithm; import std.array; import std.container; import std.stdio; import std.traits; void foo(T, U)(T collection, U elem) if(is(U : Unqual!(typeof(collection[].front { if(elem == collection.front) writefln("%s == %s", elem, collection.front); else writefln("%s != %s", elem, collection.front); } void bar(R, U)(R range, U elem) if(is(U : Unqual!(typeof(range.front { if(elem == range.front) writefln("%s == %s", elem, range.front); else writefln("%s != %s", elem, range.front); } void main() { dstring a = "hello world"; foo(a, 'h'); foo(a, 'g'); bar(a, 'h'); bar(a, 'g'); writeln(); Array!int b; b.insert(1); b.insert(2); b.insert(3); foo(b, 1); foo(b, 2); bar(b, 1); bar(b, 2); writeln(); SList!int c; c.insert(3); c.insert(2); c.insert(1); foo(c, 1); foo(c, 2); bar(c, 1); bar(c, 2); writeln(); auto d = find(c[], 2); bar(d, 1); bar(d, 2); } foo() takes a container type where you can get a range to the whole thing with a slice, and bar() works on ranges. However, it looks like all of the containers in std.container currently implement front(), so both foo() and bar() work on them. Of course, if you doing stuff that isn't on a range, then bar() wouldn't work, and foo() won't work on ranges. So, which you want probably depends on what you're trying to do. A function like bar will work on more but will be restricted to range-base operations only. And depending on what you do in a function like bar(), you might need more template constraints to constrain the type of range. I haven't tried any of this with CTFE, but the function signatures should work just fine with it. Whether you can use CTFE should therefore be a question of what you try to do in the function. In any case, I think that Unqual is the secret sauce that you're looking for here. - Jonathan M Davis
Re: Generic collection/element function signatures in D2 versus D1
On 2010-09-04 10:02, Nick Sabalausky wrote: "Nick Sabalausky" wrote in message news:i5su5e$23m...@digitalmars.com... In D1 I did this sort of thing a fair amount: void foo(T)(T[] collection, T elem) { // Blah, whatever } Worked for any of the string types, worked for any array, or anything with the appropriate opIndexes, and for all I know there may be some improvement that could still be made. But of course, in D2 strings have that extra immutable part that mucks up the above for strings (and then there's ranges), so: Is there a typical generally-best way in D2 to declare a function signature for operating on collections and elements? I know it would involve using the standard range interfaces in the body and choosing the most restrictive range type that gets the job done, and I'm fine with all that, but is there a good example of a typical "best-practice" generic-function signature in D2? Oh, also, and perhaps more importantly (I forgot, this was my main original reason for even posting the question): What would be the *right* D2 version of the above code that *didn't* bother with ranges, and just stuck with arrays and strings? Sometimes I need to do something in CTFE and using ranges leads to using "std.algorithm", and CTFE still tends to choke on a lot of "std.algorithm". If you're not going to modify the content of the array I think this will work: void foo (T) (const(T)[] collection, T elem) {} This will allow both mutable, immutable and const arrays. But it will not let you modify the array like this: collection[3] = 'a'; -- /Jacob Carlborg
Re: Generic collection/element function signatures in D2 versus D1
"Nick Sabalausky" wrote in message news:i5su5e$23m...@digitalmars.com... > In D1 I did this sort of thing a fair amount: > > void foo(T)(T[] collection, T elem) > { >// Blah, whatever > } > > Worked for any of the string types, worked for any array, or anything with > the appropriate opIndexes, and for all I know there may be some > improvement that could still be made. But of course, in D2 strings have > that extra immutable part that mucks up the above for strings (and then > there's ranges), so: Is there a typical generally-best way in D2 to > declare a function signature for operating on collections and elements? I > know it would involve using the standard range interfaces in the body and > choosing the most restrictive range type that gets the job done, and I'm > fine with all that, but is there a good example of a typical > "best-practice" generic-function signature in D2? > Oh, also, and perhaps more importantly (I forgot, this was my main original reason for even posting the question): What would be the *right* D2 version of the above code that *didn't* bother with ranges, and just stuck with arrays and strings? Sometimes I need to do something in CTFE and using ranges leads to using "std.algorithm", and CTFE still tends to choke on a lot of "std.algorithm".
Generic collection/element function signatures in D2 versus D1
In D1 I did this sort of thing a fair amount: void foo(T)(T[] collection, T elem) { // Blah, whatever } Worked for any of the string types, worked for any array, or anything with the appropriate opIndexes, and for all I know there may be some improvement that could still be made. But of course, in D2 strings have that extra immutable part that mucks up the above for strings (and then there's ranges), so: Is there a typical generally-best way in D2 to declare a function signature for operating on collections and elements? I know it would involve using the standard range interfaces in the body and choosing the most restrictive range type that gets the job done, and I'm fine with all that, but is there a good example of a typical "best-practice" generic-function signature in D2? --- Not sent from an iPhone.