digitalmars.D.learn - higher-order funcs for ranges (with usual interface)
- spir (29/29) Feb 02 2011 Hello,
- Lars T. Kyllingstad (27/49) Feb 02 2011 I don't think it's possible to do it exactly as you describe. I mean,
- Lars T. Kyllingstad (4/25) Feb 02 2011 Oops, seems i missed a few closing parentheses on the template
- spir (23/73) Feb 02 2011 Right. The output range's ElementType is given by f's return type. As yo...
- Lars T. Kyllingstad (38/122) Feb 02 2011 I'm not sure I understand what you mean here. Perhaps you're looking fo...
- spir (17/42) Feb 03 2011 Yes, I know about concepts ;-) (and typestates, and such). That's not wh...
- Lars T. Kyllingstad (3/51) Feb 03 2011 Why the reluctance to use template constraints? They're so flexible! :)
- spir (29/30) Feb 03 2011 I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily get ...
- Lars T. Kyllingstad (14/48) Feb 03 2011 I agree that is() is rather ugly. Same with __traits. If you haven't
- spir (26/74) Feb 03 2011 Done!
- Lars T. Kyllingstad (17/101) Feb 07 2011 How would you deal with the case where the input must satisfy more than
Hello, This bit of code for arrays: Out[] map (In,Out) (In[] input, Out delegate (In) f) { Out[] output = new Out[](input.length); foreach (i,item ; input) output [i] = f(item); return output; } unittest { char character (uint code) {return cast(char)code;} uint[] codes = [0x61,0x62,0x63]; // functional style writeln(map(codes, &character)); // "abc" // OO style writeln(codes.map(&character)); // "abc" } How to write this for ranges? I mean, with the same kind of interface to client code (not the interface of std.algo.map). And is there a way to write it so that it works both for ranges and other kinds of sequences (arrays, strings); or even for anything "iterable" (AA, set, list, tree...). For ranges, I'm looking for something similar to: Range!Out map (In,Out) (Range!In input, Out delegate (In) f) {...} Indeed, the compiler should understand that Range!T is a type id just like T[]. Denis -- _________________ vita es estrany spir.wikidot.com
Feb 02 2011
On Wed, 02 Feb 2011 13:26:39 +0100, spir wrote:Hello, This bit of code for arrays: Out[] map (In,Out) (In[] input, Out delegate (In) f) { Out[] output = new Out[](input.length); foreach (i,item ; input) output [i] = f(item); return output; } unittest { char character (uint code) {return cast(char)code;} uint[] codes = [0x61,0x62,0x63]; // functional style writeln(map(codes, &character)); // "abc" // OO style writeln(codes.map(&character)); // "abc" } How to write this for ranges? [...] For ranges, I'm looking for something similar to: Range!Out map (In,Out) (Range!In input, Out delegate (In) f) {...} Indeed, the compiler should understand that Range!T is a type id just like T[].I don't think it's possible to do it exactly as you describe. I mean, Range in that case can be anything, and you can't always return a range of the same kind. Two possibilities are, you can do it eagerly, Out[] map(Range, In, Out)(Range input, Out delegate(In) f) if (isInputRange!Range && is(ElementType!Range : In)) { ... } or you can do it lazily by defining your own map range (untested): struct Map(Range, In, Out) if (isInputRange!Range && is(ElementType!Range : In) { Range input; Out delegate(In) f; property bool empty() { return input.empty; } // Inefficient, should cache front... property Out front() { return f(input.front); } void popFront() { input.popFront(); } } Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In) f) if (isInputRange!R && is(ElementType!Range : In) { return typeof(return)(input, f); } -Lars
Feb 02 2011
On Wed, 02 Feb 2011 13:18:07 +0000, Lars T. Kyllingstad wrote:[...] struct Map(Range, In, Out) if (isInputRange!Range && is(ElementType!Range : In) { Range input; Out delegate(In) f; property bool empty() { return input.empty; } // Inefficient, should cache front... property Out front() { return f(input.front); } void popFront() { input.popFront(); } } Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In) f) if (isInputRange!R && is(ElementType!Range : In) { return typeof(return)(input, f); }Oops, seems i missed a few closing parentheses on the template constraints. -Lars
Feb 02 2011
On 02/02/2011 02:18 PM, Lars T. Kyllingstad wrote:On Wed, 02 Feb 2011 13:26:39 +0100, spir wrote:Right. The output range's ElementType is given by f's return type. As you say, the "kind" of range may change. Even if it's the same, how could one express that: <range_which-elements-are-of-type-T>, syntactically and in the param set, just like <array_which-elements-are-of-type-> is written "T[]"? Currently, we must (1) declare the range type as template param, which is a bit redondant because the ElementType must also be given, (2) add some 'is' horror code: if (isInputRange!Range && is(ElementType!Range : In)) I guess the only solution would be for the compiler to support a kind of reange type syntax?Hello, This bit of code for arrays: Out[] map (In,Out) (In[] input, Out delegate (In) f) { Out[] output = new Out[](input.length); foreach (i,item ; input) output [i] = f(item); return output; } unittest { char character (uint code) {return cast(char)code;} uint[] codes = [0x61,0x62,0x63]; // functional style writeln(map(codes,&character)); // "abc" // OO style writeln(codes.map(&character)); // "abc" } How to write this for ranges? [...] For ranges, I'm looking for something similar to: Range!Out map (In,Out) (Range!In input, Out delegate (In) f) {...} Indeed, the compiler should understand that Range!T is a type id just like T[].I don't think it's possible to do it exactly as you describe. I mean, Range in that case can be anything, and you can't always return a range of the same kind.Two possibilities are, you can do it eagerly, Out[] map(Range, In, Out)(Range input, Out delegate(In) f) if (isInputRange!Range&& is(ElementType!Range : In)) { ... }OK.or you can do it lazily by defining your own map range (untested): struct Map(Range, In, Out) if (isInputRange!Range&& is(ElementType!Range : In) { Range input; Out delegate(In) f; property bool empty() { return input.empty; } // Inefficient, should cache front... property Out front() { return f(input.front); } void popFront() { input.popFront(); } }That's similar to what I did.Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In) f) if (isInputRange!R&& is(ElementType!Range : In) { return typeof(return)(input, f); }What's the point of map, then? My version initially had a 'MapRange' defined as static struct template inside map, but then map just instanciated it, so I suppressed map alltogether, letting the user write: auto r2 = MapRange!(R1, In, Out)(input, f); which is not more complicated than calling the func, I guess.-LarsDenis -- _________________ vita es estrany spir.wikidot.com
Feb 02 2011
On Wed, 02 Feb 2011 18:38:02 +0100, spir wrote:On 02/02/2011 02:18 PM, Lars T. Kyllingstad wrote:I'm not sure I understand what you mean here. Perhaps you're looking for something like concepts, which have been discussed for both D and C++0x but rejected in both languages: http://en.wikipedia.org/wiki/Concept_%28generic_programming%29 Anyway, if the source and target range are of the same (known) kind, something like this should work: struct MyRange(T) { ... } MyRange!Out map(In, Out)(MyRange!In input, Out delegate(In) f) { ... } If they are of different kinds, but still known, this should work: struct MySourceRange(T) { ... } struct MyTargetRange(T) { ... } MyTargetRange!Out map(In, Out) (MySourceRange!In input, Out delegate(In) f) { ... } Note that I am only talking about what the compiler should be able to figure out through IFTI (implicit function template instantiation), and not about actual implementation.On Wed, 02 Feb 2011 13:26:39 +0100, spir wrote:Right. The output range's ElementType is given by f's return type. As you say, the "kind" of range may change. Even if it's the same, how could one express that: <range_which-elements-are-of-type-T>, syntactically and in the param set, just like <array_which-elements-are-of-type-> is written "T[]"? Currently, we must (1) declare the range type as template param, which is a bit redondant because the ElementType must also be given, (2) add some 'is' horror code: if (isInputRange!Range && is(ElementType!Range : In)) I guess the only solution would be for the compiler to support a kind of reange type syntax?Hello, This bit of code for arrays: Out[] map (In,Out) (In[] input, Out delegate (In) f) { Out[] output = new Out[](input.length); foreach (i,item ; input) output [i] = f(item); return output; } unittest { char character (uint code) {return cast(char)code;} uint[] codes = [0x61,0x62,0x63]; // functional style writeln(map(codes,&character)); // "abc" // OO style writeln(codes.map(&character)); // "abc" } How to write this for ranges? [...] For ranges, I'm looking for something similar to: Range!Out map (In,Out) (Range!In input, Out delegate (In) f) {...} Indeed, the compiler should understand that Range!T is a type id just like T[].I don't think it's possible to do it exactly as you describe. I mean, Range in that case can be anything, and you can't always return a range of the same kind.map() is just a helper function. Unlike struct literals/constructors, functions can make use of IFTI, which makes for much prettier code: // The range from my example, without map() auto result = Map!(SomeRange!int, int, bool)(someRange, someDelegate); // The range from my example, with map() auto result = map(someInputRange, someDelegate); This has become a quite common idiom in Phobos. std.range, for instance, is littered with helper functions like this: Retro, retro() Stride, stride() Chain, chain() ... The list goes on. -LarsTwo possibilities are, you can do it eagerly, Out[] map(Range, In, Out)(Range input, Out delegate(In) f) if (isInputRange!Range&& is(ElementType!Range : In)) { ... }OK.or you can do it lazily by defining your own map range (untested): struct Map(Range, In, Out) if (isInputRange!Range&& is(ElementType!Range : In) { Range input; Out delegate(In) f; property bool empty() { return input.empty; } // Inefficient, should cache front... property Out front() { return f(input.front); } void popFront() { input.popFront(); } }That's similar to what I did.Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In) f) if (isInputRange!R&& is(ElementType!Range : In) { return typeof(return)(input, f); }What's the point of map, then? My version initially had a 'MapRange' defined as static struct template inside map, but then map just instanciated it, so I suppressed map alltogether, letting the user write: auto r2 = MapRange!(R1, In, Out)(input, f); which is not more complicated than calling the func, I guess.
Feb 02 2011
On 02/03/2011 08:41 AM, Lars T. Kyllingstad wrote:On Wed, 02 Feb 2011 18:38:02 +0100, spir wrote:Yes, I know about concepts ;-) (and typestates, and such). That's not what I mean but I could not find how to express it. What I have in mind is a way to simply express <range of T> just like <array of T> is expressed by "T[]". But indeed the issue is there is only one type of array of T, while there are an infinity of types of ranges of T. Your solution below is a good alternative.I guess the only solution would be for the compiler to support a kind of reange type syntax?I'm not sure I understand what you mean here. Perhaps you're looking for something like concepts, which have been discussed for both D and C++0x but rejected in both languages: http://en.wikipedia.org/wiki/Concept_%28generic_programming%29Anyway, if the source and target range are of the same (known) kind, something like this should work: struct MyRange(T) { ... } MyRange!Out map(In, Out)(MyRange!In input, Out delegate(In) f) { ... } If they are of different kinds, but still known, this should work: struct MySourceRange(T) { ... } struct MyTargetRange(T) { ... } MyTargetRange!Out map(In, Out) (MySourceRange!In input, Out delegate(In) f) { ... } Note that I am only talking about what the compiler should be able to figure out through IFTI (implicit function template instantiation), and not about actual implementation.Right, this is more or less what I was looking for. And I think I can restrict cases to ranges beeing of the same "kind". If necessary, the result can then be mapped onto another kind of range (hopefully lazily). The only "un-workaround-able" situation is, I guess, when the source range is infinite ;-) Denis -- _________________ vita es estrany spir.wikidot.com
Feb 03 2011
On Thu, 03 Feb 2011 13:05:00 +0100, spir wrote:On 02/03/2011 08:41 AM, Lars T. Kyllingstad wrote:Why the reluctance to use template constraints? They're so flexible! :) -LarsOn Wed, 02 Feb 2011 18:38:02 +0100, spir wrote:Yes, I know about concepts ;-) (and typestates, and such). That's not what I mean but I could not find how to express it. What I have in mind is a way to simply express <range of T> just like <array of T> is expressed by "T[]". But indeed the issue is there is only one type of array of T, while there are an infinity of types of ranges of T. Your solution below is a good alternative.I guess the only solution would be for the compiler to support a kind of reange type syntax?I'm not sure I understand what you mean here. Perhaps you're looking for something like concepts, which have been discussed for both D and C++0x but rejected in both languages: http://en.wikipedia.org/wiki/Concept_%28generic_programming%29Anyway, if the source and target range are of the same (known) kind, something like this should work: struct MyRange(T) { ... } MyRange!Out map(In, Out)(MyRange!In input, Out delegate(In) f) { ... } If they are of different kinds, but still known, this should work: struct MySourceRange(T) { ... } struct MyTargetRange(T) { ... } MyTargetRange!Out map(In, Out) (MySourceRange!In input, Out delegate(In) f) { ... } Note that I am only talking about what the compiler should be able to figure out through IFTI (implicit function template instantiation), and not about actual implementation.Right, this is more or less what I was looking for. And I think I can restrict cases to ranges beeing of the same "kind". If necessary, the result can then be mapped onto another kind of range (hopefully lazily). The only "un-workaround-able" situation is, I guess, when the source range is infinite ;-)
Feb 03 2011
On 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote:Why the reluctance to use template constraints? They're so flexible! :)I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily get rid of it in favor of type-classes (built eg as an extension to current interfaces). For instance, instead of: void func (T) (T t) if (is(someConstraint1) && is(someConstraint2)) { ... } use: void func (SomeTypeClass T) (T t) { ... } For instance (untested): void func (T) (T t) if (isInputRange(T) && is(ElementType!T == E)) --> void func (InputRange!E T) (T t) where InputRange is a (templated) interface / type-class. Type-class checks on /type/ /template/ parameters (as opposed to type checks on regular value parameters) would be performed structurally (as opposed to nominally). D knows how to do this, since that's what it needs to perform when checking is() constraints. Denis -- _________________ vita es estrany spir.wikidot.com
Feb 03 2011
On Thu, 03 Feb 2011 13:53:44 +0100, spir wrote:On 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote:I agree that is() is rather ugly. Same with __traits. If you haven't already done so, I suggest you vote up this issue: http://d.puremagic.com/issues/show_bug.cgi?id=3702 Anyway, you can hide is()'s ugliness in the most common cases, though, by defining new templates. For instance, I wouldn't mind having the following in std.range as an overload of isInputRange: template isInputRange(R, T) { enum isInputRange = isInputRange!R && is(ElementType!R == T); } Then, you'd simply write void func(R)(R range) if (isInputRange!(R, E)) { ... } -LarsWhy the reluctance to use template constraints? They're so flexible! :)I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily get rid of it in favor of type-classes (built eg as an extension to current interfaces). For instance, instead of: void func (T) (T t) if (is(someConstraint1) && is(someConstraint2)) { ... } use: void func (SomeTypeClass T) (T t) { ... } For instance (untested): void func (T) (T t) if (isInputRange(T) && is(ElementType!T == E)) --> void func (InputRange!E T) (T t) where InputRange is a (templated) interface / type-class. Type-class checks on /type/ /template/ parameters (as opposed to type checks on regular value parameters) would be performed structurally (as opposed to nominally). D knows how to do this, since that's what it needs to perform when checking is() constraints.
Feb 03 2011
On 02/03/2011 02:25 PM, Lars T. Kyllingstad wrote:On Thu, 03 Feb 2011 13:53:44 +0100, spir wrote:Done! (I did not get all the details 'cause no time for a deep look, but anything impulsed by the motivation of getting rid of is() and __traits can hardly be a Bad Thing ;-) What do you think of type classes, as an alternative to Don's proposal in issue See also "Type Classes as Objects and Implicits": http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdfOn 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote:I agree that is() is rather ugly. Same with __traits. If you haven't already done so, I suggest you vote up this issue: http://d.puremagic.com/issues/show_bug.cgi?id=3702Why the reluctance to use template constraints? They're so flexible! :)I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily get rid of it in favor of type-classes (built eg as an extension to current interfaces). For instance, instead of: void func (T) (T t) if (is(someConstraint1)&& is(someConstraint2)) { ... } use: void func (SomeTypeClass T) (T t) { ... } For instance (untested): void func (T) (T t) if (isInputRange(T)&& is(ElementType!T == E)) --> void func (InputRange!E T) (T t) where InputRange is a (templated) interface / type-class. Type-class checks on /type/ /template/ parameters (as opposed to type checks on regular value parameters) would be performed structurally (as opposed to nominally). D knows how to do this, since that's what it needs to perform when checking is() constraints.Anyway, you can hide is()'s ugliness in the most common cases, though, by defining new templates. For instance, I wouldn't mind having the following in std.range as an overload of isInputRange: template isInputRange(R, T) { enum isInputRange = isInputRange!R&& is(ElementType!R == T); } Then, you'd simply write void func(R)(R range) if (isInputRange!(R, E)) { ... } -LarsA great improvement, indeed. While we're at defining a set of constraints in a template, let us make it an interface / type-class that the E must (structurally) satisfy, and just write: void func(InputRange!E R)(R range) { ... } What do you think? Note: a template is not always required, I guess: void writeElements (Iterable Elements) (Elements elements) { foreach (element, elements) { write(element,' '); } } (In this case, because write is itself generic.) Denis -- _________________ vita es estrany spir.wikidot.com
Feb 03 2011
On Thu, 03 Feb 2011 19:11:04 +0100, spir wrote:On 02/03/2011 02:25 PM, Lars T. Kyllingstad wrote:How would you deal with the case where the input must satisfy more than one concept/constraint? I mean, for the simple case where you say "R must be an input range of E", sure, type classes/concepts are cleaner. But what about the case where, say, you want R to be an infinite random access range that supports slicing? With template constraints it's simple: void doStuff(R)(R someRange) if (isRandomAccessRange!R && isInfinite!R && hasSlicing!R) { ... } Now, I'm no expert on concepts at all---my main sources of information about them are superficial comments on the D newsgroup and a quick browse of the Wikipedia page---but it seems to me that you'd have to define a new concept for each such combination of constraints. Or? -LarsOn Thu, 03 Feb 2011 13:53:44 +0100, spir wrote:Done! (I did not get all the details 'cause no time for a deep look, but anything impulsed by the motivation of getting rid of is() and __traits can hardly be a Bad Thing ;-) What do you think of type classes, as an alternative to Don's proposal See also "Type Classes as Objects and Implicits": http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdfOn 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote:I agree that is() is rather ugly. Same with __traits. If you haven't already done so, I suggest you vote up this issue: http://d.puremagic.com/issues/show_bug.cgi?id=3702Why the reluctance to use template constraints? They're so flexible! :)I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily get rid of it in favor of type-classes (built eg as an extension to current interfaces). For instance, instead of: void func (T) (T t) if (is(someConstraint1)&& is(someConstraint2)) { ... } use: void func (SomeTypeClass T) (T t) { ... } For instance (untested): void func (T) (T t) if (isInputRange(T)&& is(ElementType!T == E)) --> void func (InputRange!E T) (T t) where InputRange is a (templated) interface / type-class. Type-class checks on /type/ /template/ parameters (as opposed to type checks on regular value parameters) would be performed structurally (as opposed to nominally). D knows how to do this, since that's what it needs to perform when checking is() constraints.Anyway, you can hide is()'s ugliness in the most common cases, though, by defining new templates. For instance, I wouldn't mind having the following in std.range as an overload of isInputRange: template isInputRange(R, T) { enum isInputRange = isInputRange!R&& is(ElementType!R == T); } Then, you'd simply write void func(R)(R range) if (isInputRange!(R, E)) { ... } -LarsA great improvement, indeed. While we're at defining a set of constraints in a template, let us make it an interface / type-class that the E must (structurally) satisfy, and just write: void func(InputRange!E R)(R range) { ... } What do you think? Note: a template is not always required, I guess: void writeElements (Iterable Elements) (Elements elements) { foreach (element, elements) { write(element,' '); } } (In this case, because write is itself generic.)
Feb 07 2011
On 02/07/2011 09:18 AM, Lars T. Kyllingstad wrote:Well, dunno really. If a language implements type classes, let us see how things work there :-) Note that above, you use 3 implicite type-class defining check funcs. Agreed? Certainly, because they are common, one wrote a fucn to wrap a bigger set of is() stuff. Replacing them with a type-class interface allows (1) reusing an interface for what it's meant: defining a (super) type, instead of a func which is appropriate and does /not/ correctly convey the meaning (2) avoiding is() Now, you may be right in that type-class check may need be written externally: void doStuff(R)(R someRange) if (RandomAccessRange R && InfiniteRange R && Slicable R) or not: interface ASpecialOne : RandomAccessRange, InfiniteRange, Slicable {} void doStuff (ASpecialOneR) (R someRange) Ain't that clean? For sure, if I was to define a new static PL, I would go /that/ way for generic constraints. This remembers me about another option maybe: when I have time, I'll go and see how XL does it. If (you do not know XL, then /really/ have a look when you have time: http://en.wikipedia.org/wiki/XL_%28programming_language%29 ;esp explore the notion of "conceptual programming") Denis -- _________________ vita es estrany spir.wikidot.comHow would you deal with the case where the input must satisfy more than one concept/constraint? I mean, for the simple case where you say "R must be an input range of E", sure, type classes/concepts are cleaner. But what about the case where, say, you want R to be an infinite random access range that supports slicing? With template constraints it's simple: void doStuff(R)(R someRange) if (isRandomAccessRange!R&& isInfinite!R&& hasSlicing!R) { ... } Now, I'm no expert on concepts at all---my main sources of information about them are superficial comments on the D newsgroup and a quick browse of the Wikipedia page---but it seems to me that you'd have to define a new concept for each such combination of constraints. Or?I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happilyDone! (I did not get all the details 'cause no time for a deep look, but anything impulsed by the motivation of getting rid of is() and __traits can hardly be a Bad Thing ;-) What do you think of type classes, as an alternative to Don's proposal See also "Type Classes as Objects and Implicits": http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdfI agree that is() is rather ugly. Same with __traits. If you haven't already done so, I suggest you vote up this issue: http://d.puremagic.com/issues/show_bug.cgi?id=3702get rid of it in favor of type-classes (built eg as an extension to current interfaces). For instance, instead of: void func (T) (T t) if (is(someConstraint1)&& is(someConstraint2)) { ... } use: void func (SomeTypeClass T) (T t) { ... } For instance (untested): void func (T) (T t) if (isInputRange(T)&& is(ElementType!T == E)) --> void func (InputRange!E T) (T t) where InputRange is a (templated) interface / type-class. Type-class checks on/type/ /template/ parameters (as opposed to type checks on regular value parameters) would be performed structurally (as opposed to nominally). D knows how to do this, since that's what it needs to perform when checking is() constraints.A great improvement, indeed. While we're at defining a set of constraints in a template, let us make it an interface / type-class that the E must (structurally) satisfy, and just write: void func(InputRange!E R)(R range) { ... } What do you think? Note: a template is not always required, I guess: void writeElements (Iterable Elements) (Elements elements) { foreach (element, elements) { write(element,' '); } } (In this case, because write is itself generic.)Anyway, you can hide is()'s ugliness in the most common cases, though, by defining new templates. For instance, I wouldn't mind having the following in std.range as an overload of isInputRange: template isInputRange(R, T) { enum isInputRange = isInputRange!R&& is(ElementType!R == T); } Then, you'd simply write void func(R)(R range) if (isInputRange!(R, E)) { ... } -Lars
Feb 07 2011
If you want to use an interface as a concept, you can take kenji's adaptTo module and add this: template conformsTo(T, Interfaces...) { enum conformsTo = AdaptTo!Interfaces.hasRequiredMethods!T; } and use it like this void draw(T)(T shape) if (conformsTo!(T, Shape, Drawable)) This will of course only work for methods, not properties or aliases, so you still need constraints in some cases. Torarin
Feb 07 2011
On 02/07/2011 01:07 PM, Torarin wrote:If you want to use an interface as a concept, you can take kenji's adaptTo module and add this: template conformsTo(T, Interfaces...) { enum conformsTo = AdaptTo!Interfaces.hasRequiredMethods!T; } and use it like this void draw(T)(T shape) if (conformsTo!(T, Shape, Drawable)) This will of course only work for methods, not properties or aliases, so you still need constraints in some cases.That's nice, thank you. Denis -- _________________ vita es estrany spir.wikidot.com
Feb 07 2011