digitalmars.D.learn - Partially instantiating templates?
- Magnus Lie Hetland (37/37) Jan 31 2011 I'm building a function (or template or whatever, really) that is
- Simen kjaeraas (16/43) Jan 31 2011 ElementType!Range minArg( alias fun, Range )( Range range, out
- Magnus Lie Hetland (29/51) Jan 31 2011 Aaaah. I guess I tried ElementType(Range), forgetting to make it a
- Simen kjaeraas (52/72) Jan 31 2011 You might want to try more from dranges - the reftuple:
- Magnus Lie Hetland (13/22) Jan 31 2011 Thanks :)
- Andrej Mitrovic (3/28) Jan 31 2011 Damn! That's pretty nice, I didn't know we could nest with the
- Simen kjaeraas (4/38) Jan 31 2011 You can only do that using aliases.
- Andrej Mitrovic (40/41) Jan 31 2011 Yeah. I was just experimenting for the last half hour. I was hoping to
- Magnus Lie Hetland (14/14) Jan 31 2011 Hm. Using code quite similar to you, supplying a lambda in the second
- Simen kjaeraas (4/15) Jan 31 2011 This is a bug. Please report it.
- Magnus Lie Hetland (6/16) Feb 01 2011 Ah -- OK. Will do.
- Magnus Lie Hetland (25/40) Feb 01 2011 Hm. Just to make sure this *is* a bug, and I'm not just being a dumbass
- Simen kjaeraas (5/9) Feb 01 2011 Maybe it is. It really shouldn't be, though. If this is not a bug, then
- Magnus Lie Hetland (23/32) Feb 01 2011 Hehe :)
- Simen kjaeraas (7/10) Feb 01 2011 Not related. unaryFun and binaryFun are simply glorified string mixins,
- Magnus Lie Hetland (12/22) Feb 01 2011 That certainly makes sense. I just got thrown off by the example in
- Simen kjaeraas (5/23) Feb 01 2011 Nope, still std.functional. That's where the string is mixin'ed.
- Magnus Lie Hetland (9/22) Feb 01 2011 Right. Given the example, there's no way to tell that sort is
- Magnus Lie Hetland (20/26) Feb 01 2011 Just to be clear -- I realize I could just have used unaryFun!f here
- bearophile (4/11) Jan 31 2011 I will eventually add a detailed enhancement request on this topic.
- Magnus Lie Hetland (8/17) Feb 01 2011 Great! I think this is really useful -- also for swapping things around
- Magnus Lie Hetland (8/24) Feb 01 2011 Saw your post on digitalmars.D now, about the currying of templates
- bearophile (4/7) Feb 01 2011 Tuple unpacking syntax and template currying are two different things.
- Magnus Lie Hetland (7/14) Feb 01 2011 Yes, certainly. That was the point of this post -- that I misunderstood
- bearophile (5/8) Feb 01 2011 I will eventually add to bugzilla a request for tuple unpacking syntax, ...
I'm building a function (or template or whatever, really) that is related to map and minPos in std.algorithm. Basically, it's the standard mathematical argmin, except that it also returns min. It looks something like this: auto minArg(alias fun, Range, T)(Range range, out T minVal) { ... } Already there may be issues -- the return type should be ElementType(range) and T should be the return type of fun ... but it works. (Suggestions on these issues are welcome, but that's not really the main point here.) The thing is, because I'm also returning the actual value, I'd rather not use the strategy of std.algorithm.minPos, which asks you to use an inverted function to get maxPos; instead, I'd like an explicit maxArg function. My idea was to have a common, more general optArg, which took an operator ("<" or ">") as a compile-time argument. Then I could do something like alias optArg!"<" minArg; alias optArg!">" maxArg; Then, at some *later* time, I might want to do something like: alias maxArg!((v) {return dist(u,v);}) farthest; (By the way: For some reason, I'm not allowed to use curry(dist,u) instead of the lambda here, it seems. Any insights on that? Would have been nice to use "d(u,a)" as well -- as I do use unaryFunc on the fun argument. That doesn't work either, though...) I've been able to make either one of these two pieces of functionality work with some twiddling and nesting (i.e., *either* instantiating optArg into minArg/maxArg, *or* instantiating explicitly defined minArg/maxArg into specialized functions) but combining them has so far eluded me (unless I start fiddling with strinc constants and mixin(), which seems excessively hacky for such a simple thing). Any ideas/suggestions? I'm sure I'm missing something obvious ... (Perhaps even existing functionality for minArg/maxArg -- although the general question still stands.) -- Magnus Lie Hetland http://hetland.org
Jan 31 2011
Magnus Lie Hetland <magnus hetland.org> wrote:I'm building a function (or template or whatever, really) that is related to map and minPos in std.algorithm. Basically, it's the standard mathematical argmin, except that it also returns min. It looks something like this: auto minArg(alias fun, Range, T)(Range range, out T minVal) { ... } Already there may be issues -- the return type should be ElementType(range) and T should be the return type of fun ... but it works. (Suggestions on these issues are welcome, but that's not really the main point here.)ElementType!Range minArg( alias fun, Range )( Range range, out ReturnType!fun ) { ... } Might I also ask why you use an out parameter instead of a tuple return?The thing is, because I'm also returning the actual value, I'd rather not use the strategy of std.algorithm.minPos, which asks you to use an inverted function to get maxPos; instead, I'd like an explicit maxArg function. My idea was to have a common, more general optArg, which took an operator ("<" or ">") as a compile-time argument. Then I could do something like alias optArg!"<" minArg; alias optArg!">" maxArg; Then, at some *later* time, I might want to do something like: alias maxArg!((v) {return dist(u,v);}) farthest;D currently does not support template currying to any good degree. However, there is at least one library out there that does that for you: http://www.dsource.org/projects/dranges In the file templates.d, there is the template CurryTemplate, which rewrites a template to a set of nested templates. This would allow you to partially instantiate a template, and add more parameters as you go.I've been able to make either one of these two pieces of functionality work with some twiddling and nesting (i.e., *either* instantiating optArg into minArg/maxArg, *or* instantiating explicitly defined minArg/maxArg into specialized functions) but combining them has so far eluded me (unless I start fiddling with strinc constants and mixin(), which seems excessively hacky for such a simple thing).dranges' templates.CurryTemplate should take care of some of your problems. Not sure if it will fix them all. -- Simen
Jan 31 2011
On 2011-01-31 12:55:07 +0100, Simen kjaeraas said:ElementType!Range minArg( alias fun, Range )( Range range, out ReturnType!fun ) { ... }Aaaah. I guess I tried ElementType(Range), forgetting to make it a compile-time parameter. Thanks. (Hadn't seen ReturnType; makes sense :)Might I also ask why you use an out parameter instead of a tuple return?Well... I had a tuple return at first, but one of the advantages of returning multiple values that I'm accustomed to is the ability to assign to multiple variables, such as arg, val = minArg(...) (Yeah, I'm a Python guy... ;) As far as I can see, you can't do that here? Using result[0] and result[1] or the like, or assigning separately to two variables just seemed more cumbersome. Then again, I could use a tuple with named members, I guess. In your opinion, what would be the prettiest (in D terms) way of doing this? [snip]D currently does not support template currying to any good degree.OK. Well, I guess I don't really need it. Still trying to get a feel for what's "normal" D :)However, there is at least one library out there that does that for you: http://www.dsource.org/projects/dranges In the file templates.d, there is the template CurryTemplate, which rewrites a template to a set of nested templates. This would allow you to partially instantiate a template, and add more parameters as you go.I see. I actually don't mind writing nested templates myself -- but for some reason I couldn't get them to work properly. (D kept complaining about declarations vs instances, and the like; I guess I'll have a look at how dranges does it.)OK, thanks. By the way, if you have suggestions for other more "D-like" ways of encapsulating this functionality (basically a linear scan for an element that yields a max/min value for a given expression), I'd be interested to hear that too. The best way to solve a problem is often to rephrase it :) -- Magnus Lie Hetland http://hetland.orgI've been able to make either one of these two pieces of functionality work with some twiddling and nesting (i.e., *either* instantiating optArg into minArg/maxArg, *or* instantiating explicitly defined minArg/maxArg into specialized functions) but combining them has so far eluded me (unless I start fiddling with strinc constants and mixin(), which seems excessively hacky for such a simple thing).dranges' templates.CurryTemplate should take care of some of your problems. Not sure if it will fix them all.
Jan 31 2011
Magnus Lie Hetland <magnus hetland.org> wrote:You might want to try more from dranges - the reftuple: _(arg,val) = minArg(...); It resides in dranges.reftuple, and still has some warts, but it usually works. This is also a possible implementation (coded in about 5 minutes, gives no nice error messages, but it seems to work :p ): import std.typetuple; import std.typecons; template TypeOf( alias T ) { alias typeof( T ) TypeOf; } property void _( T... )( Tuple!( staticMap!(TypeOf, T) ) args ) { foreach ( i, e; T ) { e = args[i]; } } void main( ) { int a, b; _!(a,b) = tuple(b,a+b); // fibonacci }Might I also ask why you use an out parameter instead of a tuple return?Well... I had a tuple return at first, but one of the advantages of returning multiple values that I'm accustomed to is the ability to assign to multiple variables, such as arg, val = minArg(...) (Yeah, I'm a Python guy... ;) As far as I can see, you can't do that here? Using result[0] and result[1] or the like, or assigning separately to two variables just seemed more cumbersome. Then again, I could use a tuple with named members, I guess. In your opinion, what would be the prettiest (in D terms) way of doing this?I see. I actually don't mind writing nested templates myself -- but for some reason I couldn't get them to work properly. (D kept complaining about declarations vs instances, and the like; I guess I'll have a look at how dranges does it.)Yeah. D has the nice Eponymous Template Trick, but it sadly only works for one level. :(By the way, if you have suggestions for other more "D-like" ways of encapsulating this functionality (basically a linear scan for an element that yields a max/min value for a given expression), I'd be interested to hear that too. The best way to solve a problem is often to rephrase it :)So you have to test for every single element of the range? If so, I think this works: module foo; import std.typecons; import std.functional; import std.array; template optArg( alias pred ) { template optArg( alias fn ) { auto optArg( Range )( Range r ) { alias binaryFun!pred predicate; alias unaryFun!fn func; auto result = tuple( r.front, func( r.front ) ); foreach ( e; r ) { auto tmp = func( e ); if ( predicate( e, result[1] ) ) { result = tuple( e, tmp ); } } return result; } } } void main( ) { alias optArg!"a<b" minArg; alias minArg!"a" foo; assert( foo( [5,2,1,3] ) == tuple(1,1) ); } -- Simen
Jan 31 2011
On 2011-01-31 15:50:41 +0100, Simen kjaeraas said:You might want to try more from dranges - the reftuple: _(arg,val) = minArg(...);[snip]This is also a possible implementation (coded in about 5 minutes, gives no nice error messages, but it seems to work :p ):Thanks :)Yeah. D has the nice Eponymous Template Trick, but it sadly only works for one level. :(Right :-/So you have to test for every single element of the range?Yup.If so, I think this works:Thanks. Hm. The nesting does seem similar to how I did it, but I guess there must be some crucial difference ;-) At the moment, I'm using a mixing to create the min and max templates (as rather large strings). Probably not ideal. Thanks! -- Magnus Lie Hetland http://hetland.org
Jan 31 2011
On 1/31/11, Simen kjaeraas <simen.kjaras gmail.com> wrote:module foo; import std.typecons; import std.functional; import std.array; template optArg( alias pred ) { template optArg( alias fn ) { auto optArg( Range )( Range r ) { alias binaryFun!pred predicate; alias unaryFun!fn func; auto result = tuple( r.front, func( r.front ) ); foreach ( e; r ) { auto tmp = func( e ); if ( predicate( e, result[1] ) ) { result = tuple( e, tmp ); } } return result; } } } void main( ) { alias optArg!"a<b" minArg; alias minArg!"a" foo; assert( foo( [5,2,1,3] ) == tuple(1,1) ); }Damn! That's pretty nice, I didn't know we could nest with the eponymous trick. This could be quite useful, thanks.
Jan 31 2011
Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:On 1/31/11, Simen kjaeraas <simen.kjaras gmail.com> wrote:You can only do that using aliases. -- Simenmodule foo; import std.typecons; import std.functional; import std.array; template optArg( alias pred ) { template optArg( alias fn ) { auto optArg( Range )( Range r ) { alias binaryFun!pred predicate; alias unaryFun!fn func; auto result = tuple( r.front, func( r.front ) ); foreach ( e; r ) { auto tmp = func( e ); if ( predicate( e, result[1] ) ) { result = tuple( e, tmp ); } } return result; } } } void main( ) { alias optArg!"a<b" minArg; alias minArg!"a" foo; assert( foo( [5,2,1,3] ) == tuple(1,1) ); }Damn! That's pretty nice, I didn't know we could nest with the eponymous trick. This could be quite useful, thanks.
Jan 31 2011
On 1/31/11, Simen kjaeraas <simen.kjaras gmail.com> wrote:You can only do that using aliases.Yeah. I was just experimenting for the last half hour. I was hoping to make it easier to make an alias to a nested template using the eponymous trick. But it doesn't work at all. All I could come up with is this trickery: template optArg( alias pred ) { static class optArg2(alias fn) { static auto optArg3( Range )( Range r ) { alias binaryFun!pred predicate; alias unaryFun!fn func; auto result = tuple( r.front, func( r.front ) ); foreach ( e; r ) { auto tmp = func( e ); if ( predicate( e, result[1] ) ) { result = tuple( e, tmp ); } } return result; } static auto opCall(Range)(Range r) { return optArg3!(Range)(r); } } alias optArg2 optArg; } void main( ) { alias optArg!("a<b").optArg!("a") foo; assert( foo( [5,2,1,3] ) == tuple(1,1) ); } Of course, this won't work anymore: alias optArg!"a<b" minArg; alias minArg!"a" foo; assert( foo( [5,2,1,3] ) == tuple(1,1) );
Jan 31 2011
Hm. Using code quite similar to you, supplying a lambda in the second aliasing, I get this error: something.d(93): Error: template instance cannot use local '__dgliteral2(__T3)' as parameter to non-global template optArg(alias fun) It seems it's explicitly objecting to what I want it to do... Using optArg!"a", for example, works just fine -- but the whole point was to include some local state. Using local functions worked (I think...?) when I had a global template. It seems D's compile-time computation system is less straightforward than I thought :) -- Magnus Lie Hetland http://hetland.org
Jan 31 2011
Magnus Lie Hetland <magnus hetland.org> wrote:Hm. Using code quite similar to you, supplying a lambda in the second aliasing, I get this error: something.d(93): Error: template instance cannot use local '__dgliteral2(__T3)' as parameter to non-global template optArg(alias fun) It seems it's explicitly objecting to what I want it to do... Using optArg!"a", for example, works just fine -- but the whole point was to include some local state. Using local functions worked (I think...?) when I had a global template. It seems D's compile-time computation system is less straightforward than I thought :)This is a bug. Please report it. -- Simen
Jan 31 2011
On 2011-01-31 19:46:53 +0100, Simen kjaeraas said:Magnus Lie Hetland <magnus hetland.org> wrote:[snip]Hm. Using code quite similar to you, supplying a lambda in the second aliasing, I get this error: something.d(93): Error: template instance cannot use local '__dgliteral2(__T3)' as parameter to non-global template optArg(alias fun)This is a bug. Please report it.Ah -- OK. Will do. -- Magnus Lie Hetland http://hetland.org
Feb 01 2011
On 2011-02-01 10:12:44 +0100, Magnus Lie Hetland said:On 2011-01-31 19:46:53 +0100, Simen kjaeraas said:Hm. Just to make sure this *is* a bug, and I'm not just being a dumbass ... this is a tiny program that illustrates the problem (i.e., gives the error above). Perhaps the use of a local function here really is prohibited...? template A(int op) { template A(alias fun) { auto A(T)(T x) { return 0; } } } alias A!0 B; int gun() { return 0; } void main() { int fun() {return 0;} // alias B!((){return 0;}) C; // Won't compile // alias B!(fun) C; // Won't compile alias B!(gun) C; // Works } -- Magnus Lie Hetland http://hetland.orgMagnus Lie Hetland <magnus hetland.org> wrote:[snip]Hm. Using code quite similar to you, supplying a lambda in the second aliasing, I get this error: something.d(93): Error: template instance cannot use local '__dgliteral2(__T3)' as parameter to non-global template optArg(alias fun)This is a bug. Please report it.Ah -- OK. Will do.
Feb 01 2011
Magnus Lie Hetland <magnus hetland.org> wrote:Hm. Just to make sure this *is* a bug, and I'm not just being a dumbass ... this is a tiny program that illustrates the problem (i.e., gives the error above). Perhaps the use of a local function here really is prohibited...?Maybe it is. It really shouldn't be, though. If this is not a bug, then Walter has a bug for not accepting this as a bug. :p -- Simen
Feb 01 2011
On 2011-02-01 12:37:05 +0100, Simen kjaeraas said:Magnus Lie Hetland <magnus hetland.org> wrote:Hehe :) Sort of related (though perhaps only remotely) is the following, which won't compile (Error: static assert "Bad unary function: f(a) for type int"): import std.functional, std.stdio; int f(int x) {return x;} void main() { alias unaryFun!("f(a)") g; writeln(g(3)); } It may not be related -- but I've been trying to use the string representation instead of lambda in some places, and I thought maybe a similar name lookup problem may be present in the unaryFun template? (The detauls of the implementation are a bit beyond me at the moment...) Maybe there's an unstated restriction against using functions in the unaryFun string parameter (at least I couldn't find it in the docs) -- but ... there is the following example in the docs for std.algorithms: sort!("hashFun(a) < hashFun(b)")(array); So it would seem like this *should* work? -- Magnus Lie Hetland http://hetland.orgHm. Just to make sure this *is* a bug, and I'm not just being a dumbass ... this is a tiny program that illustrates the problem (i.e., gives the error above). Perhaps the use of a local function here really is prohibited...?Maybe it is. It really shouldn't be, though. If this is not a bug, then Walter has a bug for not accepting this as a bug. :p
Feb 01 2011
Magnus Lie Hetland <magnus hetland.org> wrote:Sort of related (though perhaps only remotely) is the following, which won't compile (Error: static assert "Bad unary function: f(a) for type int"):Not related. unaryFun and binaryFun are simply glorified string mixins, and thus can only access functions that are available in the modules where they are mixed in. That would be std.functional. Because of that, local functions may not be used as string arguments for *naryFun. -- Simen
Feb 01 2011
On 2011-02-01 16:09:22 +0100, Simen kjaeraas said:Magnus Lie Hetland <magnus hetland.org> wrote:That certainly makes sense. I just got thrown off by the example in std.algorithm: uint hashFun(string) { ... expensive computation ... } string[] array = ...; // Sort strings by hash, slow sort!("hashFun(a) < hashFun(b)")(array); The only way this could work would be if hashFun was available to the sort template, I guess...? -- Magnus Lie Hetland http://hetland.orgSort of related (though perhaps only remotely) is the following, which won't compile (Error: static assert "Bad unary function: f(a) for type int"):Not related. unaryFun and binaryFun are simply glorified string mixins, and thus can only access functions that are available in the modules where they are mixed in. That would be std.functional. Because of that, local functions may not be used as string arguments for *naryFun.
Feb 01 2011
Magnus Lie Hetland <magnus hetland.org> wrote:On 2011-02-01 16:09:22 +0100, Simen kjaeraas said:Nope, still std.functional. That's where the string is mixin'ed. -- SimenMagnus Lie Hetland <magnus hetland.org> wrote:That certainly makes sense. I just got thrown off by the example in std.algorithm: uint hashFun(string) { ... expensive computation ... } string[] array = ...; // Sort strings by hash, slow sort!("hashFun(a) < hashFun(b)")(array); The only way this could work would be if hashFun was available to the sort template, I guess...?Sort of related (though perhaps only remotely) is the following, which won't compile (Error: static assert "Bad unary function: f(a) for type int"):Not related. unaryFun and binaryFun are simply glorified string mixins, and thus can only access functions that are available in the modules where they are mixed in. That would be std.functional. Because of that, local functions may not be used as string arguments for *naryFun.
Feb 01 2011
On 2011-02-01 16:41:33 +0100, Simen kjaeraas said:Right. Given the example, there's no way to tell that sort is implemented using std.functional, so really meant whichever function is actually using the string ;) But, yeah, I understand how it works. Thanks.That certainly makes sense. I just got thrown off by the example in std.algorithm: uint hashFun(string) { ... expensive computation ... } string[] array = ...; // Sort strings by hash, slow sort!("hashFun(a) < hashFun(b)")(array); The only way this could work would be if hashFun was available to the sort template, I guess...?Nope, still std.functional. That's where the string is mixin'ed.Good. -- Magnus Lie Hetland http://hetland.org
Feb 01 2011
On 2011-02-01 16:00:16 +0100, Magnus Lie Hetland said:import std.functional, std.stdio; int f(int x) {return x;} void main() { alias unaryFun!("f(a)") g; writeln(g(3)); }Just to be clear -- I realize I could just have used unaryFun!f here (or just f, for that matter). The usage case was actually currying. I used "f(x,a)" as a compile-time argument to the kind of template that we discussed earlier. And the reason I tried that was that this didn't work either: import std.functional, std.stdio; int f(int x, int y) {return x;} void main() { alias unaryFun!(curry(f, 2)) g; writeln(g(3)); } At that point, the only thing that worked was using a lambda. And, as I pointed out, with the nested templates, that didn't work either. Seems like the language (or the stdlib) is resisting my efforts at every turn here. Perhaps I should just write out those for-loops redundantly, rather than using templates ;) -- Magnus Lie Hetland http://hetland.org
Feb 01 2011
Magnus Lie Hetland:Well... I had a tuple return at first, but one of the advantages of returning multiple values that I'm accustomed to is the ability to assign to multiple variables, such as arg, val = minArg(...) (Yeah, I'm a Python guy... ;)I will eventually add a detailed enhancement request on this topic. Bye, bearophile
Jan 31 2011
On 2011-01-31 22:21:40 +0100, bearophile said:Magnus Lie Hetland:[snip]Great! I think this is really useful -- also for swapping things around (a, b = b, a). For multiple return values it makes a huge difference, IMO. -- Magnus Lie Hetland http://hetland.orgI'm accustomed to is the ability to assign to multiple variables, such as arg, val = minArg(...) (Yeah, I'm a Python guy... ;)I will eventually add a detailed enhancement request on this topic.
Feb 01 2011
On 2011-02-01 10:11:53 +0100, Magnus Lie Hetland said:On 2011-01-31 22:21:40 +0100, bearophile said:Saw your post on digitalmars.D now, about the currying of templates (i.e., the main topic here). I guess perhaps that was what you were talking about? That would certainly be great, too :) -- Magnus Lie Hetland http://hetland.orgMagnus Lie Hetland:[snip]Great! I think this is really useful -- also for swapping things around (a, b = b, a). For multiple return values it makes a huge difference, IMO.I'm accustomed to is the ability to assign to multiple variables, such as arg, val = minArg(...) (Yeah, I'm a Python guy... ;)I will eventually add a detailed enhancement request on this topic.
Feb 01 2011
Magnus Lie Hetland:Saw your post on digitalmars.D now, about the currying of templates (i.e., the main topic here). I guess perhaps that was what you were talking about?Tuple unpacking syntax and template currying are two different things. Bye, bearophile
Feb 01 2011
On 2011-02-01 10:49:23 +0100, bearophile said:Magnus Lie Hetland:Yes, certainly. That was the point of this post -- that I misunderstood what you were talking about in the original post (where you said "this topic" right after my tuple unpacking paragraph) :) -- Magnus Lie Hetland http://hetland.orgSaw your post on digitalmars.D now, about the currying of templates (i.e., the main topic here). I guess perhaps that was what you were talking about?Tuple unpacking syntax and template currying are two different things.
Feb 01 2011
Magnus Lie Hetland:Yes, certainly. That was the point of this post -- that I misunderstood what you were talking about in the original post (where you said "this topic" right after my tuple unpacking paragraph) :)I will eventually add to bugzilla a request for tuple unpacking syntax, see here: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=118601 Bye, bearophile
Feb 01 2011