digitalmars.D - memoize
- Andrei Alexandrescu (7/7) Jan 03 2011 I just added a higher-order function memoize to std.functional which I
- Nick Sabalausky (3/10) Jan 03 2011 Neat! This is a great example of why D kicks so much ass :)
- Guilherme Vieira (9/24) Jan 03 2011 Uh, yes. It looks like the kind of thing I would do, show to others and ...
- Andrei Alexandrescu (15/41) Jan 03 2011 Glad you folks like it. There's a little story behind this. I first read...
- Jonathan M Davis (3/54) Jan 03 2011 Well, it's definitely cool.
- Guilherme Vieira (24/77) Jan 03 2011 Is there really need for ParameterTypeTuple? I figured this works:
- Andrei Alexandrescu (5/9) Jan 04 2011 [snip]
- Simen kjaeraas (4/16) Jan 04 2011 So the solution should use a struct with overloaded opCall, then.
- Andrei Alexandrescu (5/21) Jan 04 2011 1. I think it's best to map a function to a function for best impedance
- Simen kjaeraas (4/5) Jan 04 2011 Yeah. Even I managed to think about that a few seconds after posting. :p
- Guilherme Vieira (30/116) Jan 03 2011 Dirty fix for the ubyte[4] problem:
- %u (11/11) Jan 04 2011 Hi,
- Andrei Alexandrescu (10/21) Jan 04 2011 Wow. I swear I didn't see this! "Great minds think alike" eh? :o)
- %u (12/14) Jan 04 2011 something. Instantiating memoize!sqrt latches on sqrt(float). We need to
- %u (14/14) Jan 04 2011 Hi all,
- Andrei Alexandrescu (8/22) Jan 04 2011 There's still the risk of keeping multiple hashes. Consider:
- %u (5/11) Jan 04 2011 Ohhh I see... so you're basically looking for a compile-time version of ...
- Michal Minich (4/7) Jan 04 2011 Also outlook express, omea reader, pan and there many more nntp new
- Jonathan M Davis (8/26) Jan 04 2011 I signed up for the mailing list and use kmail as my mail client. But if...
- Andrei Alexandrescu (5/16) Jan 04 2011 It's not that complicated; we'll be able to achieve it somehow.
- Lars T. Kyllingstad (61/79) Jan 05 2011 Since this is likely to be useful in other cases as well, maybe it would...
- Max Samukha (14/93) Jan 05 2011 __traits(getOverloads) does work with module level functions if you pass...
- Lars T. Kyllingstad (46/81) Jan 05 2011 Cool! I didn't know that. Here's an updated version, where
- Max Samukha (4/85) Jan 05 2011 Nice! Though I think that overloads should be selected not by type
- Lars T. Kyllingstad (16/47) Jan 05 2011 I agree. It is actually trivial to modify my example by replacing
- Max Samukha (2/49) Jan 05 2011 I agree with every point. Now time for a compiler patch ;)
- %u (11/11) Jan 04 2011 It seems to me that this is an unsolvable problem without built-in compi...
- Andrei Alexandrescu (6/17) Jan 04 2011 Just posted a response to that thread, too. Somehow all that thread
- Simen kjaeraas (5/27) Jan 04 2011 I took the liberty of spilling it:
- spir (8/11) Jan 04 2011 =20
- Andrei Alexandrescu (16/26) Jan 04 2011 An example in Higher Order Perl is memoizing a RGB to CMYK function:
- Manfred_Nowak (3/4) Jan 04 2011 Since when is it legal to return a local array?
- Andrei Alexandrescu (3/7) Jan 04 2011 Since a few releases ago fixed-size arrays are value types.
- Manfred_Nowak (3/4) Jan 04 2011 upps. sorry for not reading the docs.
- Andrei Alexandrescu (14/18) Jan 04 2011 BTW I think the rgb2cmyk implementation in Higher Order Perl (which I
- Steven Schveighoffer (10/30) Jan 04 2011 Note that in the current compiler, this produces a heap allocation.
- Tomek =?ISO-8859-2?Q?Sowi=F1ski?= (9/24) Jan 04 2011 I didn't know you have to put them:)
- Tomek =?ISO-8859-2?Q?Sowi=F1ski?= (9/18) Jan 04 2011 tion on the change log... Was that changed?
- Nick Sabalausky (7/10) Jan 04 2011 Since D2, if it's a static array (as result is):
- Andrew Wiley (5/11) Jan 03 2011 Pretty sweet, but if I'm understanding this correctly, the memoized vers...
- Guilherme Vieira (12/29) Jan 03 2011 memoize uses a static variable. Are static variables considered "global"...
- Jonathan M Davis (19/53) Jan 03 2011
- Guilherme Vieira (11/78) Jan 03 2011 Ah, I was under the impression that @trusted was used to force purity.
- Jonathan M Davis (29/37) Jan 03 2011 @safe, @trusted, and @system have nothing to do with purity. If a functi...
- Guilherme Vieira (19/76) Jan 03 2011 Don't take me too seriously since I'm learning those things on-the-fly h...
- Simen kjaeraas (15/18) Jan 04 2011 The simplest solution is this:
- Andrei Alexandrescu (18/34) Jan 04 2011 I've been mulling over this for a while, maybe it's time to start
- bearophile (4/13) Jan 04 2011 http://d.puremagic.com/issues/show_bug.cgi?id=5125
- bearophile (5/7) Jan 04 2011 It looks acceptable. Many solutions are better than the current need to ...
- Michel Fortin (8/11) Jan 04 2011 One could even argue that memoize makes impure functions pure because
- Nick Sabalausky (3/10) Jan 04 2011 ...until maxSize is exceeded and the cache is cleared.
- Andrei Alexandrescu (4/17) Jan 04 2011 Even before that. There may be a visible difference between the first
- bearophile (5/6) Jan 04 2011 Clearing the whole cache when it's full is a brutal behaviour, I don't l...
- Andrei Alexandrescu (4/10) Jan 04 2011 There is a difference between caching and memoization. I wouldn't want
- bearophile (7/9) Jan 04 2011 Yep, memoization is a special case of caching:
- Andrei Alexandrescu (4/13) Jan 04 2011 Removing a random element behaves surprisingly well and crossed my mind
- Michel Fortin (8/21) Jan 04 2011 Right, of course. I was speaking about the case where there is no
- Jonathan M Davis (28/69) Jan 04 2011 We really do need something like this, and actually I was thinking of br...
- bearophile (12/15) Jan 04 2011 I am not sure it's a good idea to put inside the D docs a link to the Go...
- Eric Poggel (3/10) Jan 04 2011 If it's helpful to anyone, Yage has a similar D1 function called Cache:
- Eric Poggel (2/4) Jan 04 2011 I almost forgot about licensing. I release this module under public
I just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here: http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoize I'm also thinking of adding that cutting-edge directory as a place for storing documentation for commits that are in flux but not officially released yet. Andrei
Jan 03 2011
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:ifu70u$2dvs$1 digitalmars.com...I just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here: http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoize I'm also thinking of adding that cutting-edge directory as a place for storing documentation for commits that are in flux but not officially released yet. AndreiNeat! This is a great example of why D kicks so much ass :)
Jan 03 2011
On Tue, Jan 4, 2011 at 2:50 AM, Nick Sabalausky <a a.a> wrote:"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:ifu70u$2dvs$1 digitalmars.com...Uh, yes. It looks like the kind of thing I would do, show to others and hear they say "Meh.. Whatever". I'm really exponentially developing a liking to the D community, even though I didn't event get to code anything serious yet. This simply rocks. Keep it up, Andrei! -- Atenciosamente / Sincerely, Guilherme ("n2liquid") VieiraI just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here:http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoizeI'm also thinking of adding that cutting-edge directory as a place for storing documentation for commits that are in flux but not officially released yet. AndreiNeat! This is a great example of why D kicks so much ass :)
Jan 03 2011
On 1/3/11 11:01 PM, Guilherme Vieira wrote:On Tue, Jan 4, 2011 at 2:50 AM, Nick Sabalausky <a a.a> wrote: "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>> wrote in message news:ifu70u$2dvs$1 digitalmars.com... >I just added a higher-order function memoize to std.functional which I >think is pretty cool. See the docs here: > > http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoize > > I'm also thinking of adding that cutting-edge directory as a place for > storing documentation for commits that are in flux but not officially > released yet. > > > Andrei Neat! This is a great example of why D kicks so much ass :) Uh, yes. It looks like the kind of thing I would do, show to others and hear they say "Meh.. Whatever". I'm really exponentially developing a liking to the D community, even though I didn't event get to code anything serious yet. This simply rocks. Keep it up, Andrei! -- Atenciosamente / Sincerely, Guilherme ("n2liquid") VieiraGlad you folks like it. There's a little story behind this. I first read Dominus' book chapter years ago, around the time I'd decided to write TDPL. Back then I was thinking - it would just be so cool to be able to define generic memoization in D. I tried my hand at an implementation. But D had no tuples, no aliasing for functions, no good variadics, and even if you could find a way to pack parameters, associative arrays had plenty of related issues. I'd given up on that and forgot most about it, until today. It was nice to reckon that getting it done took about a dozen lines and about as many minutes. We really have come a very long way. Nevertheless, I found two issues: one, ParameterTypeTuple doesn't work for overloaded functions, and associative arrays don't work for ubyte[4] keys... still a ways to go. Andrei
Jan 03 2011
On Monday 03 January 2011 21:27:22 Andrei Alexandrescu wrote:On 1/3/11 11:01 PM, Guilherme Vieira wrote:Well, it's definitely cool. - Jonathan M DavisOn Tue, Jan 4, 2011 at 2:50 AM, Nick Sabalausky <a a.a> wrote: "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>> wrote in message news:ifu70u$2dvs$1 digitalmars.com... >I just added a higher-order function memoize to std.functional >which I >think is pretty cool. See the docs here: http://d-programming-language.org/cutting-edge/phobos/std_functional. html#memoize > I'm also thinking of adding that cutting-edge directory as a place for > storing documentation for commits that are in flux but not > officially released yet. > > > Andrei Neat! This is a great example of why D kicks so much ass :) Uh, yes. It looks like the kind of thing I would do, show to others and hear they say "Meh.. Whatever". I'm really exponentially developing a liking to the D community, even though I didn't event get to code anything serious yet. This simply rocks. Keep it up, Andrei! -- Atenciosamente / Sincerely, Guilherme ("n2liquid") VieiraGlad you folks like it. There's a little story behind this. I first read Dominus' book chapter years ago, around the time I'd decided to write TDPL. Back then I was thinking - it would just be so cool to be able to define generic memoization in D. I tried my hand at an implementation. But D had no tuples, no aliasing for functions, no good variadics, and even if you could find a way to pack parameters, associative arrays had plenty of related issues. I'd given up on that and forgot most about it, until today. It was nice to reckon that getting it done took about a dozen lines and about as many minutes. We really have come a very long way. Nevertheless, I found two issues: one, ParameterTypeTuple doesn't work for overloaded functions, and associative arrays don't work for ubyte[4] keys... still a ways to go.
Jan 03 2011
On Tue, Jan 4, 2011 at 3:27 AM, Andrei Alexandrescu < SeeWebsiteForEmail erdani.org> wrote:On 1/3/11 11:01 PM, Guilherme Vieira wrote:Is there really need for ParameterTypeTuple? I figured this works: template memoize(alias fun, uint maxSize = uint.max) { auto memoize(Args...)(Args args) { static typeof(fn(args))[Tuple!(typeof(args))] memo; auto t = tuple(args); auto p = t in memo; if (p) return *p; static if (maxSize != uint.max) { if (memo.length >= maxSize) memo = null; } auto r = fun(args); //writeln("Inserting result ", typeof(r).stringof, "(", r, ") forOn Tue, Jan 4, 2011 at 2:50 AM, Nick Sabalausky <a a.a> wrote: "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>> wrote in message news:ifu70u$2dvs$1 digitalmars.com... >I just added a higher-order function memoize to std.functional which I >think is pretty cool. See the docs here: > > http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoize > > I'm also thinking of adding that cutting-edge directory as a place for > storing documentation for commits that are in flux but not officially > released yet. > > > Andrei Neat! This is a great example of why D kicks so much ass :) Uh, yes. It looks like the kind of thing I would do, show to others and hear they say "Meh.. Whatever". I'm really exponentially developing a liking to the D community, even though I didn't event get to code anything serious yet. This simply rocks. Keep it up, Andrei! -- Atenciosamente / Sincerely, Guilherme ("n2liquid") VieiraGlad you folks like it. There's a little story behind this. I first read Dominus' book chapter years ago, around the time I'd decided to write TDPL. Back then I was thinking - it would just be so cool to be able to define generic memoization in D. I tried my hand at an implementation. But D had no tuples, no aliasing for functions, no good variadics, and even if you could find a way to pack parameters, associative arrays had plenty of related issues. I'd given up on that and forgot most about it, until today. It was nice to reckon that getting it done took about a dozen lines and about as many minutes. We really have come a very long way. Nevertheless, I found two issues: one, ParameterTypeTuple doesn't work for overloaded functions, and associative arrays don't work for ubyte[4] keys... still a ways to go. Andreiparameters ", t);memo[t] = r; return r; } } -- Atenciosamente / Sincerely, Guilherme ("n2liquid") Vieira
Jan 03 2011
On 1/4/11 12:30 AM, Guilherme Vieira wrote:Is there really need for ParameterTypeTuple? I figured this works: template memoize(alias fun, uint maxSize = uint.max) { auto memoize(Args...)(Args args)[snip] That would create several caches, depending on calls with convertible arguments. Andrei
Jan 04 2011
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 1/4/11 12:30 AM, Guilherme Vieira wrote:So the solution should use a struct with overloaded opCall, then. -- SimenIs there really need for ParameterTypeTuple? I figured this works: template memoize(alias fun, uint maxSize = uint.max) { auto memoize(Args...)(Args args)[snip] That would create several caches, depending on calls with convertible arguments. Andrei
Jan 04 2011
On 1/4/11 7:54 AM, Simen kjaeraas wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:1. I think it's best to map a function to a function for best impedance match. 2. If you memoize an overloaded function, you _do_ want several caches. AndreiOn 1/4/11 12:30 AM, Guilherme Vieira wrote:So the solution should use a struct with overloaded opCall, then.Is there really need for ParameterTypeTuple? I figured this works: template memoize(alias fun, uint maxSize = uint.max) { auto memoize(Args...)(Args args)[snip] That would create several caches, depending on calls with convertible arguments. Andrei
Jan 04 2011
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:2. If you memoize an overloaded function, you _do_ want several caches.Yeah. Even I managed to think about that a few seconds after posting. :p -- Simen
Jan 04 2011
On Tue, Jan 4, 2011 at 4:30 AM, Guilherme Vieira <n2.nitrogen gmail.com>wrote:On Tue, Jan 4, 2011 at 3:27 AM, Andrei Alexandrescu < SeeWebsiteForEmail erdani.org> wrote:Dirty fix for the ubyte[4] problem: struct wrapper(T) { this(T value) { this.value = value; } T value; }On 1/3/11 11:01 PM, Guilherme Vieira wrote:Is there really need for ParameterTypeTuple? I figured this works: template memoize(alias fun, uint maxSize = uint.max) { auto memoize(Args...)(Args args) { static typeof(fn(args))[Tuple!(typeof(args))] memo; auto t = tuple(args); auto p = t in memo; if (p) return *p; static if (maxSize != uint.max) { if (memo.length >= maxSize) memo = null; } auto r = fun(args); //writeln("Inserting result ", typeof(r).stringof, "(", r, ") forOn Tue, Jan 4, 2011 at 2:50 AM, Nick Sabalausky <a a.a> wrote: "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>> wrote in message news:ifu70u$2dvs$1 digitalmars.com... >I just added a higher-order function memoize to std.functional which I >think is pretty cool. See the docs here: > > http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoize > > I'm also thinking of adding that cutting-edge directory as a place for > storing documentation for commits that are in flux but not officially > released yet. > > > Andrei Neat! This is a great example of why D kicks so much ass :) Uh, yes. It looks like the kind of thing I would do, show to others and hear they say "Meh.. Whatever". I'm really exponentially developing a liking to the D community, even though I didn't event get to code anything serious yet. This simply rocks. Keep it up, Andrei! -- Atenciosamente / Sincerely, Guilherme ("n2liquid") VieiraGlad you folks like it. There's a little story behind this. I first read Dominus' book chapter years ago, around the time I'd decided to write TDPL. Back then I was thinking - it would just be so cool to be able to define generic memoization in D. I tried my hand at an implementation. But D had no tuples, no aliasing for functions, no good variadics, and even if you could find a way to pack parameters, associative arrays had plenty of related issues. I'd given up on that and forgot most about it, until today. It was nice to reckon that getting it done took about a dozen lines and about as many minutes. We really have come a very long way. Nevertheless, I found two issues: one, ParameterTypeTuple doesn't work for overloaded functions, and associative arrays don't work for ubyte[4] keys... still a ways to go. Andreiparameters ", t);memo[t] = r; return r; } } -- Atenciosamente / Sincerely, Guilherme ("n2liquid") Vieiratemplate memoize(alias fun, uint maxSize = uint.max){ auto memoize(Args...)(Args args) { static wrapper!(typeof(fn(args)))[Tuple!(typeof(args))] memo; auto t = tuple(args); auto p = t in memo; if (p) return p.value; static if (maxSize != uint.max) { if (memo.length >= maxSize) memo = null; } auto r = fun(args); //writeln("Inserting result ", typeof(r).stringof, "(", r, ") forparameters ", t);memo[t] = wrapper!(typeof(fn(args)))(r); return r; } } But I think I get why ParameterTypeTuple is important. My version of the template give ugly error messages when I call a memoized function with the wrong parameter types. ParameterTypeTuple would make them go away, right? -- Atenciosamente / Sincerely, Guilherme ("n2liquid") Vieira
Jan 03 2011
Hi, I just wanted to say, it's funny how I did the *EXACT* same thing only **a few days ago**: Not counting the braces, the actual code is just 4 lines for the compile-time version, and 5 lines for the run-time version. The page above only has a link to one of these; both of the versions are here: http://ideone.com/q8lvf Pretty short, eh? I doubt that it can get any shorter than that code. :) Feel free to use my version inside the library. ("I hereby release the code to the public domain," to cover the legal stuff.)
Jan 04 2011
On 1/4/11 2:48 AM, %u wrote:Hi, I just wanted to say, it's funny how I did the *EXACT* same thing only **a few days ago**: Not counting the braces, the actual code is just 4 lines for the compile-time version, and 5 lines for the run-time version. The page above only has a link to one of these; both of the versions are here: http://ideone.com/q8lvf Pretty short, eh? I doubt that it can get any shorter than that code. :) Feel free to use my version inside the library. ("I hereby release the code to the public domain," to cover the legal stuff.)Wow. I swear I didn't see this! "Great minds think alike" eh? :o) I think your implementation that defines the function directly is better. However, when we plan to make the whole thing work with overloading, we'll probably want to go with the template solution. BTW, right now ParameterTypeTuple catches the lexically first overload or something. Instantiating memoize!sqrt latches on sqrt(float). We need to generalize ParameterTypeTuple to work with overloaded functions. This is where implicit flattening of tuples is going to turn a bit troublesome. Andrei
Jan 04 2011
Andrei:"Great minds think alike" eh? :o)Definitely! :]BTW, right now ParameterTypeTuple catches the lexically first overload orsomething. Instantiating memoize!sqrt latches on sqrt(float). We need to generalize ParameterTypeTuple to work with overloaded functions. This is where implicit flattening of tuples is going to turn a bit troublesome. Huh... I didn't know that. There *should* be a way to make the user get around it, such as forcing him to supply the argument list in the template. I'll see if I can find a fix for it. By the way, did you look at my run-time implementation? If you leave a compile-time version in the library, I highly suggest you leave the run-time version as well, since it can come handy if the code is external and not known at compile-time.
Jan 04 2011
Hi all, How does this work, with respect to overloading? auto memo(alias Fn, TParams...)(TParams args) if (isCallable!(Fn)) { static typeof(Func(args))[Tuple!(TParams)] cache; auto key = tuple(args); return key in cache ? cache[key] : (cache[key] = Func(args)); } uint fib(uint n) { return n > 1 ? memo!(fib)(n - 1) + memo!(fib)(n - 2) : n; } ulong fib(ulong n) { return n > 1 ? memo!(fib)(n - 1) + memo!(fib)(n - 2) : n; } void main() { memoize(&fib); std.stdio.writefln("%d", fib(50)); } It seems like it's fixed, right?
Jan 04 2011
On 1/4/11 2:57 PM, %u wrote:Hi all, How does this work, with respect to overloading? auto memo(alias Fn, TParams...)(TParams args) if (isCallable!(Fn)) { static typeof(Func(args))[Tuple!(TParams)] cache; auto key = tuple(args); return key in cache ? cache[key] : (cache[key] = Func(args)); } uint fib(uint n) { return n> 1 ? memo!(fib)(n - 1) + memo!(fib)(n - 2) : n; } ulong fib(ulong n) { return n> 1 ? memo!(fib)(n - 1) + memo!(fib)(n - 2) : n; } void main() { memoize(&fib); std.stdio.writefln("%d", fib(50)); } It seems like it's fixed, right?There's still the risk of keeping multiple hashes. Consider: ulong fun(ulong n) { ... } alias memoize!fun mfun; mfun(5); // creates hash ulong[int] mfun(5u); // creates hash ulong[uint] mfun('5'); // creates hash ulong[char] Andrei
Jan 04 2011
There's still the risk of keeping multiple hashes. Consider:ulong fun(ulong n) { ... } alias memoize!fun mfun;mfun(5); // creates hash ulong[int] mfun(5u); // creates hash ulong[uint] mfun('5'); // creates hash ulong[char]Ohhh I see... so you're basically looking for a compile-time version of overload resolution, right? Because things seem to be getting complicated very quickly. (An unrelated side note: I'm new to newsgroups, and I was wondering, what program do people mainly use for communication? I'm using the web interface, but do most people use Thunderbird or something? Thank you!)
Jan 04 2011
On Tue, 04 Jan 2011 22:49:11 +0000, %u wrote:(An unrelated side note: I'm new to newsgroups, and I was wondering, what program do people mainly use for communication? I'm using the web interface, but do most people use Thunderbird or something? Thank you!)Also outlook express, omea reader, pan and there many more nntp new readers; any is much better than the web interface (which is used mainly as a temp solution or to make web links to posts).
Jan 04 2011
On Tuesday, January 04, 2011 14:49:11 %u wrote:I signed up for the mailing list and use kmail as my mail client. But if you use the mailing list, then you can use whatever works with your e-mail service, which would presumably include whatever mail clients work on your system. I was using knode (KDE's newsreader program), but then the state of messages (like which you've read and the like) is only on one machine. With the mailing list and IMAP, I can have it synced between machines. - Jonathan M DavisThere's still the risk of keeping multiple hashes. Consider: ulong fun(ulong n) { ... } alias memoize!fun mfun; mfun(5); // creates hash ulong[int] mfun(5u); // creates hash ulong[uint] mfun('5'); // creates hash ulong[char]Ohhh I see... so you're basically looking for a compile-time version of overload resolution, right? Because things seem to be getting complicated very quickly. (An unrelated side note: I'm new to newsgroups, and I was wondering, what program do people mainly use for communication? I'm using the web interface, but do most people use Thunderbird or something? Thank you!)
Jan 04 2011
On 1/4/11 4:49 PM, %u wrote:It's not that complicated; we'll be able to achieve it somehow. Overloads are compile-time entities so they should be easily inspectable.There's still the risk of keeping multiple hashes. Consider:ulong fun(ulong n) { ... } alias memoize!fun mfun;mfun(5); // creates hash ulong[int] mfun(5u); // creates hash ulong[uint] mfun('5'); // creates hash ulong[char]Ohhh I see... so you're basically looking for a compile-time version of overload resolution, right? Because things seem to be getting complicated very quickly.(An unrelated side note: I'm new to newsgroups, and I was wondering, what program do people mainly use for communication? I'm using the web interface, but do most people use Thunderbird or something? Thank you!)I use Thunderbird with NNTP, which is better than the Web interface. Andrei
Jan 04 2011
On Tue, 04 Jan 2011 17:06:45 -0600, Andrei Alexandrescu wrote:On 1/4/11 4:49 PM, %u wrote:Since this is likely to be useful in other cases as well, maybe it would be better to make a general template that selects an overload? So you'd use it like this: alias memoize!(selectOverload!(sqrt, double)) msqrt; A first step towards this would be to make __traits(getOverloads) work with module-level functions. Currently it only works for member functions. Also it would probably be better if it accepted a function alias instead of a string containing the function name. Anyway, I've been able to make the following work for selecting overloads of member functions: import std.traits, std.stdio; struct S { void foo(int i, double d) { writeln("Overload 1"); } void foo(double d, int i) { writeln("Overload 2"); } void test() { alias selectOverload!(S, "foo", int, double) bar; bar(1, 3.0); // Prints "Overload 1" alias selectOverload!(S, "foo", double, int) baz; baz(4.0, 2); // Prints "Overload 2" } } void main() { S s; s.test(); } template selectOverload(T, string fun, Params...) { alias selectOverloadImpl!(Params.length, Params, __traits(getOverloads, T, fun)) selectOverload; } template selectOverloadImpl(size_t lenP, A...) { static assert (A.length > lenP, "No overload matches"); static if (equalTuples!(lenP, A[0 .. lenP], ParameterTypeTuple!(A[lenP]))) { alias A[lenP] selectOverloadImpl; } else { alias selectOverloadImpl!(lenP, A[0 .. lenP], A[lenP+1 .. $]) selectOverloadImpl; } } template equalTuples(size_t len, T...) { static if (len == 0 && T.length == 0) enum equalTuples = true; else static if (T.length != len*2) enum equalTuples = false; else static if (!is (T[0] == T[len])) enum equalTuples = false; else enum equalTuples = equalTuples!(len-1, T[1 .. len], T[len+1 .. $]); }It's not that complicated; we'll be able to achieve it somehow. Overloads are compile-time entities so they should be easily inspectable.There's still the risk of keeping multiple hashes. Consider:ulong fun(ulong n) { ... } alias memoize!fun mfun;mfun(5); // creates hash ulong[int] mfun(5u); // creates hash ulong[uint] mfun('5'); // creates hash ulong[char]Ohhh I see... so you're basically looking for a compile-time version of overload resolution, right? Because things seem to be getting complicated very quickly.
Jan 05 2011
On 01/05/2011 11:21 AM, Lars T. Kyllingstad wrote:On Tue, 04 Jan 2011 17:06:45 -0600, Andrei Alexandrescu wrote:__traits(getOverloads) does work with module level functions if you pass a module alias to it. I predict you guys will soon need (and I am happy you are approaching this way-point) a means to get from a symbol to its parent, that is __traits(parent, symbol). So there are two ways to solve the problem: 1. Add a version of __traits(getOverloads) that would not require a parent as you suggested. 2. Add __traits(parent), so one could do: void foo(); void foo(int); alias __traits(getOverloads, __traits(parent, foo), "foo") fooOverloads; I prefer the second or both, because the parent trait is needed in other scenarios.On 1/4/11 4:49 PM, %u wrote:Since this is likely to be useful in other cases as well, maybe it would be better to make a general template that selects an overload? So you'd use it like this: alias memoize!(selectOverload!(sqrt, double)) msqrt; A first step towards this would be to make __traits(getOverloads) work with module-level functions. Currently it only works for member functions. Also it would probably be better if it accepted a function alias instead of a string containing the function name.It's not that complicated; we'll be able to achieve it somehow. Overloads are compile-time entities so they should be easily inspectable.There's still the risk of keeping multiple hashes. Consider:ulong fun(ulong n) { ... } alias memoize!fun mfun;mfun(5); // creates hash ulong[int] mfun(5u); // creates hash ulong[uint] mfun('5'); // creates hash ulong[char]Ohhh I see... so you're basically looking for a compile-time version of overload resolution, right? Because things seem to be getting complicated very quickly.Anyway, I've been able to make the following work for selecting overloads of member functions: import std.traits, std.stdio; struct S { void foo(int i, double d) { writeln("Overload 1"); } void foo(double d, int i) { writeln("Overload 2"); } void test() { alias selectOverload!(S, "foo", int, double) bar; bar(1, 3.0); // Prints "Overload 1" alias selectOverload!(S, "foo", double, int) baz; baz(4.0, 2); // Prints "Overload 2" } } void main() { S s; s.test(); } template selectOverload(T, string fun, Params...) { alias selectOverloadImpl!(Params.length, Params, __traits(getOverloads, T, fun)) selectOverload; } template selectOverloadImpl(size_t lenP, A...) { static assert (A.length> lenP, "No overload matches"); static if (equalTuples!(lenP, A[0 .. lenP], ParameterTypeTuple!(A[lenP]))) { alias A[lenP] selectOverloadImpl; } else { alias selectOverloadImpl!(lenP, A[0 .. lenP], A[lenP+1 .. $]) selectOverloadImpl; } } template equalTuples(size_t len, T...) { static if (len == 0&& T.length == 0) enum equalTuples = true; else static if (T.length != len*2) enum equalTuples = false; else static if (!is (T[0] == T[len])) enum equalTuples = false; else enum equalTuples = equalTuples!(len-1, T[1 .. len], T[len+1 .. $]); }
Jan 05 2011
On Wed, 05 Jan 2011 12:07:50 +0200, Max Samukha wrote:On 01/05/2011 11:21 AM, Lars T. Kyllingstad wrote:Cool! I didn't know that. Here's an updated version, where selectOverload works with both module-level and member functions: module test; import std.traits, std.stdio; void foo(int i, double d) { writeln("Overload 1"); } void foo(double d, int i) { writeln("Overload 2"); } void main() { alias selectOverload!(test, "foo", int, double) bar; bar(1, 3.0); // Prints "Overload 1" alias selectOverload!(test, "foo", double, int) baz; baz(4.0, 2); // Prints "Overload 2" } template selectOverload(alias parent, string fun, Params...) { alias selectOverloadImpl!(Params.length, Params, __traits(getOverloads, parent, fun)) selectOverload; } template selectOverloadImpl(size_t lenP, A...) { static assert (A.length > lenP, "No overload matches"); static if (equalTuples!(lenP, A[0 .. lenP], ParameterTypeTuple!(A[lenP]))) { alias A[lenP] selectOverloadImpl; } else { alias selectOverloadImpl!(lenP, A[0 .. lenP], A[lenP+1 .. $]) selectOverloadImpl; } } template equalTuples(size_t len, T...) { static if (len == 0 && T.length == 0) enum equalTuples = true; else static if (T.length != len*2) enum equalTuples = false; else static if (!is (T[0] == T[len])) enum equalTuples = false; else enum equalTuples = equalTuples!(len-1, T[1 .. len], T[len+1 .. $]); }On Tue, 04 Jan 2011 17:06:45 -0600, Andrei Alexandrescu wrote:__traits(getOverloads) does work with module level functions if you pass a module alias to it.On 1/4/11 4:49 PM, %u wrote:Since this is likely to be useful in other cases as well, maybe it would be better to make a general template that selects an overload? So you'd use it like this: alias memoize!(selectOverload!(sqrt, double)) msqrt; A first step towards this would be to make __traits(getOverloads) work with module-level functions. Currently it only works for member functions. Also it would probably be better if it accepted a function alias instead of a string containing the function name.It's not that complicated; we'll be able to achieve it somehow. Overloads are compile-time entities so they should be easily inspectable.There's still the risk of keeping multiple hashes. Consider:ulong fun(ulong n) { ... } alias memoize!fun mfun;mfun(5); // creates hash ulong[int] mfun(5u); // creates hash ulong[uint] mfun('5'); // creates hash ulong[char]Ohhh I see... so you're basically looking for a compile-time version of overload resolution, right? Because things seem to be getting complicated very quickly.
Jan 05 2011
On 01/05/2011 12:24 PM, Lars T. Kyllingstad wrote:On Wed, 05 Jan 2011 12:07:50 +0200, Max Samukha wrote:Nice! Though I think that overloads should be selected not by type equality but by regular overload resolution rules. That is the template should take not parameter but argument types.On 01/05/2011 11:21 AM, Lars T. Kyllingstad wrote:Cool! I didn't know that. Here's an updated version, where selectOverload works with both module-level and member functions: module test; import std.traits, std.stdio; void foo(int i, double d) { writeln("Overload 1"); } void foo(double d, int i) { writeln("Overload 2"); } void main() { alias selectOverload!(test, "foo", int, double) bar; bar(1, 3.0); // Prints "Overload 1" alias selectOverload!(test, "foo", double, int) baz; baz(4.0, 2); // Prints "Overload 2" } template selectOverload(alias parent, string fun, Params...) { alias selectOverloadImpl!(Params.length, Params, __traits(getOverloads, parent, fun)) selectOverload; } template selectOverloadImpl(size_t lenP, A...) { static assert (A.length> lenP, "No overload matches"); static if (equalTuples!(lenP, A[0 .. lenP], ParameterTypeTuple!(A[lenP]))) { alias A[lenP] selectOverloadImpl; } else { alias selectOverloadImpl!(lenP, A[0 .. lenP], A[lenP+1 .. $]) selectOverloadImpl; } } template equalTuples(size_t len, T...) { static if (len == 0&& T.length == 0) enum equalTuples = true; else static if (T.length != len*2) enum equalTuples = false; else static if (!is (T[0] == T[len])) enum equalTuples = false; else enum equalTuples = equalTuples!(len-1, T[1 .. len], T[len+1 .. $]); }On Tue, 04 Jan 2011 17:06:45 -0600, Andrei Alexandrescu wrote:__traits(getOverloads) does work with module level functions if you pass a module alias to it.On 1/4/11 4:49 PM, %u wrote:Since this is likely to be useful in other cases as well, maybe it would be better to make a general template that selects an overload? So you'd use it like this: alias memoize!(selectOverload!(sqrt, double)) msqrt; A first step towards this would be to make __traits(getOverloads) work with module-level functions. Currently it only works for member functions. Also it would probably be better if it accepted a function alias instead of a string containing the function name.It's not that complicated; we'll be able to achieve it somehow. Overloads are compile-time entities so they should be easily inspectable.There's still the risk of keeping multiple hashes. Consider:ulong fun(ulong n) { ... } alias memoize!fun mfun;mfun(5); // creates hash ulong[int] mfun(5u); // creates hash ulong[uint] mfun('5'); // creates hash ulong[char]Ohhh I see... so you're basically looking for a compile-time version of overload resolution, right? Because things seem to be getting complicated very quickly.
Jan 05 2011
On Wed, 05 Jan 2011 13:40:05 +0200, Max Samukha wrote:On 01/05/2011 12:24 PM, Lars T. Kyllingstad wrote:I agree. It is actually trivial to modify my example by replacing equalTuple!() with implicitlyConvertibleTuple!() which uses an is(T1:T2) test instead of is(T1==T2). But that would make the template select the first possible match, instead of the "best match" resolution used by the compiler, which is often not what you want. It would fail miserably for the sqrt() example, for instance, by always picking out sqrt(float). In the end, I think implementing full overload resolution in the library will be so hairy it will be better to just add __traits(selectOverload). This doesn't look too bad: alias memoize!(__traits(selectOverload, sqrt, double)) msqrt; And if, one day, __traits gets replaced by the meta namespace (bug 3702, one of the highest-voted in Bugzilla), it actually looks pretty good: alias memoize!(meta.selectOverload(sqrt, double)) msqrt; ;) -LarsOn Wed, 05 Jan 2011 12:07:50 +0200, Max Samukha wrote:Nice! Though I think that overloads should be selected not by type equality but by regular overload resolution rules. That is the template should take not parameter but argument types.On 01/05/2011 11:21 AM, Lars T. Kyllingstad wrote:Cool! I didn't know that. Here's an updated version, where selectOverload works with both module-level and member functions: [...]On Tue, 04 Jan 2011 17:06:45 -0600, Andrei Alexandrescu wrote:__traits(getOverloads) does work with module level functions if you pass a module alias to it.[...] Overloads are compile-time entities so they should be easily inspectable.Since this is likely to be useful in other cases as well, maybe it would be better to make a general template that selects an overload? So you'd use it like this: alias memoize!(selectOverload!(sqrt, double)) msqrt; A first step towards this would be to make __traits(getOverloads) work with module-level functions. Currently it only works for member functions. Also it would probably be better if it accepted a function alias instead of a string containing the function name.
Jan 05 2011
On 01/05/2011 02:12 PM, Lars T. Kyllingstad wrote:On Wed, 05 Jan 2011 13:40:05 +0200, Max Samukha wrote:I agree with every point. Now time for a compiler patch ;)On 01/05/2011 12:24 PM, Lars T. Kyllingstad wrote:I agree. It is actually trivial to modify my example by replacing equalTuple!() with implicitlyConvertibleTuple!() which uses an is(T1:T2) test instead of is(T1==T2). But that would make the template select the first possible match, instead of the "best match" resolution used by the compiler, which is often not what you want. It would fail miserably for the sqrt() example, for instance, by always picking out sqrt(float). In the end, I think implementing full overload resolution in the library will be so hairy it will be better to just add __traits(selectOverload). This doesn't look too bad: alias memoize!(__traits(selectOverload, sqrt, double)) msqrt; And if, one day, __traits gets replaced by the meta namespace (bug 3702, one of the highest-voted in Bugzilla), it actually looks pretty good: alias memoize!(meta.selectOverload(sqrt, double)) msqrt; ;) -LarsOn Wed, 05 Jan 2011 12:07:50 +0200, Max Samukha wrote:Nice! Though I think that overloads should be selected not by type equality but by regular overload resolution rules. That is the template should take not parameter but argument types.On 01/05/2011 11:21 AM, Lars T. Kyllingstad wrote:Cool! I didn't know that. Here's an updated version, where selectOverload works with both module-level and member functions: [...]On Tue, 04 Jan 2011 17:06:45 -0600, Andrei Alexandrescu wrote:__traits(getOverloads) does work with module level functions if you pass a module alias to it.[...] Overloads are compile-time entities so they should be easily inspectable.Since this is likely to be useful in other cases as well, maybe it would be better to make a general template that selects an overload? So you'd use it like this: alias memoize!(selectOverload!(sqrt, double)) msqrt; A first step towards this would be to make __traits(getOverloads) work with module-level functions. Currently it only works for member functions. Also it would probably be better if it accepted a function alias instead of a string containing the function name.
Jan 05 2011
It seems to me that this is an unsolvable problem without built-in compiler support using __traits(getOverloads, Func), since without knowing all the overloads of a function, you can't possibly write a template to widen the arguments to the correct parameter types. (The current getOverloads needs a class argument, but we need one without.) Another solution would be another form of an "is" expression that gives you the compile-time parameter type tuples of a called method, like: static if (is(Func(args) ParamTypeTuple == function)) { ... } Then we can get the type tuples of the overload of the function *after* the binding, rather than *before*.
Jan 04 2011
On 1/4/11 2:48 AM, %u wrote:Hi, I just wanted to say, it's funny how I did the *EXACT* same thing only **a few days ago**: Not counting the braces, the actual code is just 4 lines for the compile-time version, and 5 lines for the run-time version. The page above only has a link to one of these; both of the versions are here: http://ideone.com/q8lvf Pretty short, eh? I doubt that it can get any shorter than that code. :) Feel free to use my version inside the library. ("I hereby release the code to the public domain," to cover the legal stuff.)Just posted a response to that thread, too. Somehow all that thread should spill into reddit :o). One thing one figures by looking at that thread is not what's been done, but how much bigger the potential is. Andrei
Jan 04 2011
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 1/4/11 2:48 AM, %u wrote:I took the liberty of spilling it: http://www.reddit.com/r/programming/comments/ew0c7/coolest_template_metaprogramming_tricks_in_d/ -- SimenHi, I just wanted to say, it's funny how I did the *EXACT* same thing only **a few days ago**: Not counting the braces, the actual code is just 4 lines for the compile-time version, and 5 lines for the run-time version. The page above only has a link to one of these; both of the versions are here: http://ideone.com/q8lvf Pretty short, eh? I doubt that it can get any shorter than that code. :) Feel free to use my version inside the library. ("I hereby release the code to the public domain," to cover the legal stuff.)Just posted a response to that thread, too. Somehow all that thread should spill into reddit :o).
Jan 04 2011
On Mon, 03 Jan 2011 23:27:22 -0600 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Nevertheless, I found two issues: one, ParameterTypeTuple doesn't work=20 for overloaded functions, and associative arrays don't work for ubyte[4]==20keys... still a ways to go.Could you or someone else expand on this? Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Jan 04 2011
On 1/4/11 6:32 AM, spir wrote:On Mon, 03 Jan 2011 23:27:22 -0600 Andrei Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:An example in Higher Order Perl is memoizing a RGB to CMYK function: ubyte[4] rgb2cmyk(ubyte[3] rgb) { ubyte[4] result = void; result[0] = cast(ubyte)(255 - rgb[0]); result[1] = cast(ubyte)(255 - rgb[1]); result[2] = cast(ubyte)(255 - rgb[2]); result[3] = min(result[0], result[1], result[2]); result[0] -= result[3]; result[1] -= result[3]; result[2] -= result[3]; return result; } Trying to memoize this yields a range error at runtime. AndreiNevertheless, I found two issues: one, ParameterTypeTuple doesn't work for overloaded functions, and associative arrays don't work for ubyte[4] keys... still a ways to go.Could you or someone else expand on this? Denis -- -- -- -- -- -- -- vit esse estrany ☣ spir.wikidot.com
Jan 04 2011
Andrei Alexandrescu wrote:return result;Since when is it legal to return a local array? -manfred
Jan 04 2011
On 1/4/11 12:13 PM, Manfred_Nowak wrote:Andrei Alexandrescu wrote:Since a few releases ago fixed-size arrays are value types. Andreireturn result;Since when is it legal to return a local array? -manfred
Jan 04 2011
Andrei Alexandrescu wrote:Since a few releases agoupps. sorry for not reading the docs. -manfred
Jan 04 2011
On 1/4/11 12:44 PM, Manfred_Nowak wrote:Andrei Alexandrescu wrote:BTW I think the rgb2cmyk implementation in Higher Order Perl (which I copied) is a bit convoluted. A simplified version is: ubyte[4] rgb2cmyk(ubyte[3] rgb) { immutable m = max(rgb[0], rgb[1], rgb[2]); return [ cast(ubyte)(m - rgb[0]), cast(ubyte)(m - rgb[1]), cast(ubyte)(m - rgb[2]), ~m ]; } Two nice typing touches: max does not necessitate a cast because it's implemented to return ubyte for ubytes, and ~m also doesn't need a cast thanks to value range propagation. I don't know how to get rid of the remaining casts. AndreiSince a few releases agoupps. sorry for not reading the docs. -manfred
Jan 04 2011
On Tue, 04 Jan 2011 14:16:27 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 1/4/11 12:44 PM, Manfred_Nowak wrote:Note that in the current compiler, this produces a heap allocation. But if that is fixed, the following at least reduces the casts: ubyte[4] rbg2cmyk(ubyte[3] rgb) { immutable m = max(rgb[0], rgb[1], rgb[2]); return cast(ubyte[4])[m-rgb[0], m-rgb[1], m-rgb[2], ~m]; } -SteveAndrei Alexandrescu wrote:BTW I think the rgb2cmyk implementation in Higher Order Perl (which I copied) is a bit convoluted. A simplified version is: ubyte[4] rgb2cmyk(ubyte[3] rgb) { immutable m = max(rgb[0], rgb[1], rgb[2]); return [ cast(ubyte)(m - rgb[0]), cast(ubyte)(m - rgb[1]), cast(ubyte)(m - rgb[2]), ~m ]; } Two nice typing touches: max does not necessitate a cast because it's implemented to return ubyte for ubytes, and ~m also doesn't need a cast thanks to value range propagation. I don't know how to get rid of the remaining casts.Since a few releases agoupps. sorry for not reading the docs. -manfred
Jan 04 2011
Andrei Alexandrescu napisa=B3:BTW I think the rgb2cmyk implementation in Higher Order Perl (which I=20 copied) is a bit convoluted. A simplified version is: =20 ubyte[4] rgb2cmyk(ubyte[3] rgb) { immutable m =3D max(rgb[0], rgb[1], rgb[2]); return [ cast(ubyte)(m - rgb[0]), cast(ubyte)(m - rgb[1]), cast(ubyte)(m - rgb[2]), ~m ]; } =20 Two nice typing touches: max does not necessitate a cast because it's=20 implemented to return ubyte for ubytes, and ~m also doesn't need a cast=20 thanks to value range propagation. =20 I don't know how to get rid of the remaining casts.I didn't know you have to put them:) http://www.digitalmars.com/d/2.0/expression.html#ArrayLiteral "The type of the first element is taken to be the type of all the elements,= and all elements are implicitly converted to that type." But now (2.051) I tested and it seems it goes for the common type. No menti= on on the change log... Was that changed? If so, why? --=20 Tomek
Jan 04 2011
Tomek Sowi=F1ski napisa=B3:I didn't know you have to put them:) =20 http://www.digitalmars.com/d/2.0/expression.html#ArrayLiteral =20 "The type of the first element is taken to be the type of all the element=s, and all elements are implicitly convertedto that type." =20 But now (2.051) I tested and it seems it goes for the common type. No men=tion on the change log... Was that changed?If so, why?OK, got it: http://www.digitalmars.com/d/archives/digitalmars/D/array_literal_element_t= ypes_100812.html Docs need fixing, though. --=20 Tomek
Jan 04 2011
"Manfred_Nowak" <svv1999 hotmail.com> wrote in message news:Xns9E63C3202449Bsvv1999hotmailcom 65.204.18.192...Andrei Alexandrescu wrote:Since D2, if it's a static array (as result is): "Static arrays are value types. Unlike in C and D version 1, static arrays are passed to functions by value. Static arrays can also be returned by functions." - http://www.digitalmars.com/d/2.0/arrays.html#static-arraysreturn result;Since when is it legal to return a local array?
Jan 04 2011
On Mon, Jan 3, 2011 at 10:15 PM, Andrei Alexandrescu < SeeWebsiteForEmail erdani.org> wrote:I just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here: http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoize I'm also thinking of adding that cutting-edge directory as a place for storing documentation for commits that are in flux but not officially released yet.Pretty sweet, but if I'm understanding this correctly, the memoized version of a pure function isn't pure, is it? Is there any way to get that to happen?
Jan 03 2011
On Tue, Jan 4, 2011 at 4:05 AM, Andrew Wiley <debio264 gmail.com> wrote:On Mon, Jan 3, 2011 at 10:15 PM, Andrei Alexandrescu < SeeWebsiteForEmail erdani.org> wrote:memoize uses a static variable. Are static variables considered "global" as far as pure functions are concerned? If not, then I see no reason for it not to be pure, or am I missing something? Additionally, I don't understand this: "Technically the memoized function should be pure because memoize assumes itI just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here: http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoize I'm also thinking of adding that cutting-edge directory as a place for storing documentation for commits that are in flux but not officially released yet.Pretty sweet, but if I'm understanding this correctly, the memoized version of a pure function isn't pure, is it? Is there any way to get that to happen?will always return the same result for a given tuple of arguments. However, memoize does not enforce that because sometimes it is useful to memoize an impure function, too."Wouldn't memoizing an impure function be a.. bug? It would return the same cached value when in fact it should have returned something else based on external factors. When can that be desirable? -- Atenciosamente / Sincerely, Guilherme ("n2liquid") Vieira
Jan 03 2011
On Monday 03 January 2011 22:17:52 Guilherme Vieira wrote:On Tue, Jan 4, 2011 at 4:05 AM, Andrew Wiley <debio264 gmail.com> wrote:pure functions cannot access mutable globals or static variables. If they could, they could end up returning a different result with the same parameters because the global or static variable changed.On Mon, Jan 3, 2011 at 10:15 PM, Andrei Alexandrescu < SeeWebsiteForEmail erdani.org> wrote:memoize uses a static variable. Are static variables considered "global" as far as pure functions are concerned? If not, then I see no reason for it not to be pure, or am I missing something?I just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here: http://d-programming-language.org/cutting-edge/phobos/std_functional.htm l#memoize I'm also thinking of adding that cutting-edge directory as a place for storing documentation for commits that are in flux but not officially released yet.Pretty sweet, but if I'm understanding this correctly, the memoized version of a pure function isn't pure, is it? Is there any way to get that to happen?Additionally, I don't understand this: "Technically the memoized function should be pure because memoize assumes itIt's quite easy to have a function which you cannot actually make pure but which you know to be logically pure. For instance, if a function called a C function, it can't be pure unless you play some games with function pointers and casting and the like. So, really, any function which calls a C function can't be pure. However, it's pretty easy to have a C function which is logically pure. So, your D function is then logically pure, but it's not actually pure, so pure functions can't call it. But it would work just fine with memoize. Sure, it's definitely less safe to use impure functions with memoize, and it could cause bugs, but that doesn't mean that it shouldn't necessarily be disallowed. It just means that if the programmer wants to use memoize, they're going to have to be aware that it could cause bugs if the function in question should actually be returning a different value on future callls. - Jonathan M Daviswill always return the same result for a given tuple of arguments. However, memoize does not enforce that because sometimes it is useful to memoize an impure function, too."Wouldn't memoizing an impure function be a.. bug? It would return the same cached value when in fact it should have returned something else based on external factors. When can that be desirable?
Jan 03 2011
On Tue, Jan 4, 2011 at 4:48 AM, Jonathan M Davis <jmdavisProg gmx.com>wrote:On Monday 03 January 2011 22:17:52 Guilherme Vieira wrote:Ah, I was under the impression that trusted was used to force purity. Shame.. Sorry I mixed things up. In any case, aren't those rules a bit too rigid? No matter how you look at memoize, it should generate pure functions (since it exhibits pure nature, are logically pure or whatever you call it). Would it be tough on the compiler for it to actually find if a function is pure or not by a more detailed analysis of its behavior? -- Atenciosamente / Sincerely, Guilherme ("n2liquid") VieiraOn Tue, Jan 4, 2011 at 4:05 AM, Andrew Wiley <debio264 gmail.com> wrote:http://d-programming-language.org/cutting-edge/phobos/std_functional.htmOn Mon, Jan 3, 2011 at 10:15 PM, Andrei Alexandrescu < SeeWebsiteForEmail erdani.org> wrote:I just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here:asmemoize uses a static variable. Are static variables considered "global"l#memoize I'm also thinking of adding that cutting-edge directory as a place for storing documentation for commits that are in flux but not officially released yet.Pretty sweet, but if I'm understanding this correctly, the memoized version of a pure function isn't pure, is it? Is there any way to get that to happen?far as pure functions are concerned? If not, then I see no reason for it not to be pure, or am I missing something?pure functions cannot access mutable globals or static variables. If they could, they could end up returning a different result with the same parameters because the global or static variable changed.Additionally, I don't understand this: "Technically the memoized function should be pure because memoize assumes ittowill always return the same result for a given tuple of arguments. However, memoize does not enforce that because sometimes it is usefulsamememoize an impure function, too."Wouldn't memoizing an impure function be a.. bug? It would return thecached value when in fact it should have returned something else based on external factors. When can that be desirable?It's quite easy to have a function which you cannot actually make pure but which you know to be logically pure. For instance, if a function called a C function, it can't be pure unless you play some games with function pointers and casting and the like. So, really, any function which calls a C function can't be pure. However, it's pretty easy to have a C function which is logically pure. So, your D function is then logically pure, but it's not actually pure, so pure functions can't call it. But it would work just fine with memoize. Sure, it's definitely less safe to use impure functions with memoize, and it could cause bugs, but that doesn't mean that it shouldn't necessarily be disallowed. It just means that if the programmer wants to use memoize, they're going to have to be aware that it could cause bugs if the function in question should actually be returning a different value on future callls. - Jonathan M Davis
Jan 03 2011
On Monday 03 January 2011 22:59:37 Guilherme Vieira wrote:Ah, I was under the impression that trusted was used to force purity. Shame.. Sorry I mixed things up. In any case, aren't those rules a bit too rigid? No matter how you look at memoize, it should generate pure functions (since it exhibits pure nature, are logically pure or whatever you call it). Would it be tough on the compiler for it to actually find if a function is pure or not by a more detailed analysis of its behavior?safe, trusted, and system have nothing to do with purity. If a function is marked as pure, then it's pure. If it's not, then it's not. End of story. Now, the concept of "weakly" pure functions was recently added, but all pure functions are still marked with pure. Strongly pure functions are pure functions where all of the parameters are immutable or implicitly convertible to immutable (so, immutable variables and value types). Weakly pure functions are pure functions whose parameters are not all immutable or implicitly convertible to immutable. Unlike strongly pure functions, they can alter their arguments. However, just like strongly pure functions, they cannot access globals or statics. Strongly pure functions can be optimized such that only one call to them is made in an expression because their return value will always be the same, and they cannot alter their parameters. Weakly pure functions cannot be optimized in that manner, but because they don't alter the global state, they can be safely called from strongly pure functions without violating the strongly pure function's purity. Pure is not logically pure for essentially the same reasons that const is not logically const. Making it logical instead of guaranteed eliminates the guarantees, and the compiler can no longer rely on those guarantees, making them _far_ less valuable. It is true that a memoized function must be logically pure, or you're going to get errors. However, if you were to force such functions to be pure, it would be highly limiting. And since memoize _can't_ be pure, it would make recursion with memoize impossible. True, allowing for memoized functions to be impure makes it so that the programmer must be more careful, but it's still quite useful - in fact more useful - that way. And since you _can't_ have memoize be pure anyway, it's not like you're losing any guarantees from the compiler like if you tried to make pure logically pure or const logically const. - Jonathan M Davis
Jan 03 2011
On Tue, Jan 4, 2011 at 5:28 AM, Jonathan M Davis <jmdavisProg gmx.com>wrote:On Monday 03 January 2011 22:59:37 Guilherme Vieira wrote:Don't take me too seriously since I'm learning those things on-the-fly here (I'm greedy). The const FAQ ( http://www.digitalmars.com/d/2.0/const-faq.html#logical-const) says D doesn't support logical const because it would break transitivity, and they deemed transitivity to be more important (Walter even thinks it's bad, doesn't he?), so I understand why const in D is not logical const. But in the case of memoize, aren't there things that can be done to the language spec to allow it to be pure? Maybe by adding some language construct, I dunno. Or by analysis of behavior. Obviously, that wouldn't work for external C functions, but at least memoize would be okay. Also, I'm to guess it would be extremely hard to write a compiler that does this, but I'd just like to confirm. Walter: would it be hard/impossible for the compiler to look at memoize and tell it exhibits pure behavior and is, thus, pure? -- Atenciosamente / Sincerely, Guilherme ("n2liquid") VieiraAh, I was under the impression that trusted was used to force purity. Shame.. Sorry I mixed things up. In any case, aren't those rules a bit too rigid? No matter how you lookatmemoize, it should generate pure functions (since it exhibits purenature,are logically pure or whatever you call it). Would it be tough on the compiler for it to actually find if a function is pure or not by a more detailed analysis of its behavior?safe, trusted, and system have nothing to do with purity. If a function is marked as pure, then it's pure. If it's not, then it's not. End of story. Now, the concept of "weakly" pure functions was recently added, but all pure functions are still marked with pure. Strongly pure functions are pure functions where all of the parameters are immutable or implicitly convertible to immutable (so, immutable variables and value types). Weakly pure functions are pure functions whose parameters are not all immutable or implicitly convertible to immutable. Unlike strongly pure functions, they can alter their arguments. However, just like strongly pure functions, they cannot access globals or statics. Strongly pure functions can be optimized such that only one call to them is made in an expression because their return value will always be the same, and they cannot alter their parameters. Weakly pure functions cannot be optimized in that manner, but because they don't alter the global state, they can be safely called from strongly pure functions without violating the strongly pure function's purity. Pure is not logically pure for essentially the same reasons that const is not logically const. Making it logical instead of guaranteed eliminates the guarantees, and the compiler can no longer rely on those guarantees, making them _far_ less valuable. It is true that a memoized function must be logically pure, or you're going to get errors. However, if you were to force such functions to be pure, it would be highly limiting. And since memoize _can't_ be pure, it would make recursion with memoize impossible. True, allowing for memoized functions to be impure makes it so that the programmer must be more careful, but it's still quite useful - in fact more useful - that way. And since you _can't_ have memoize be pure anyway, it's not like you're losing any guarantees from the compiler like if you tried to make pure logically pure or const logically const. - Jonathan M Davis
Jan 03 2011
Guilherme Vieira <n2.nitrogen gmail.com> wrote:Walter: would it be hard/impossible for the compiler to look at memoize and tell it exhibits pure behavior and is, thus, pure?The simplest solution is this: template memoize( alias fn ) { static if ( isPure!fn ) { pure auto memoize( ParameterTypeTuple!fn ) { // Blah! } } else { auto memoize( ParameterTypeTuple!fn ) { // Blah! } } } -- Simen
Jan 04 2011
On 1/4/11 7:59 AM, Simen kjaeraas wrote:Guilherme Vieira <n2.nitrogen gmail.com> wrote:I've been mulling over this for a while, maybe it's time to start discussing it. Often you want to say "this entity is pure/const/immutable/safe... if this other entity is the same, or generally if this Boolean is true". So I was thinking of introducing e.g. a constrained pure: template memoize( alias fn ) { pure(isPure!fn) auto memoize( ParameterTypeTuple!fn ) { ... } } So generally when you write "attribute(expression)" the attribute will be in effect if and only if the expression is true. D has become very powerful at introspecting most of its own abstractions in the form of compile-time Booleans. This language extension would close the circle by allowing D to introduce attributes and qualifiers depending on compile-time Booleans. AndreiWalter: would it be hard/impossible for the compiler to look at memoize and tell it exhibits pure behavior and is, thus, pure?The simplest solution is this: template memoize( alias fn ) { static if ( isPure!fn ) { pure auto memoize( ParameterTypeTuple!fn ) { // Blah! } } else { auto memoize( ParameterTypeTuple!fn ) { // Blah! } } }
Jan 04 2011
Andrei:Often you want to say "this entity is pure/const/immutable/safe... if this other entity is the same, or generally if this Boolean is true". So I was thinking of introducing e.g. a constrained pure: template memoize( alias fn ) { pure(isPure!fn) auto memoize( ParameterTypeTuple!fn ) { ... } }http://d.puremagic.com/issues/show_bug.cgi?id=5125 Bye, bearophile
Jan 04 2011
Andrei:So generally when you write "attribute(expression)" the attribute will be in effect if and only if the expression is true.It looks acceptable. Many solutions are better than the current need to duplicate code :-) Regarding the purity of memoize, a kind of compiler-blessed built-in memoization for pure functuions allows pure memoization... (It is a trusted pure, where the trusted entity is the compiler) Bye, bearophile
Jan 04 2011
On 2011-01-04 12:53:43 -0500, bearophile <bearophileHUGS lycos.com> said:Regarding the purity of memoize, a kind of compiler-blessed built-in memoization for pure functuions allows pure memoization... (It is a trusted pure, where the trusted entity is the compiler)One could even argue that memoize makes impure functions pure because as long as you don't clear the cache, memoize!func is guarantied to return the same value for the same arguments. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jan 04 2011
"Michel Fortin" <michel.fortin michelf.com> wrote in message news:ifvo9g$2d53$1 digitalmars.com...On 2011-01-04 12:53:43 -0500, bearophile <bearophileHUGS lycos.com> said:...until maxSize is exceeded and the cache is cleared.Regarding the purity of memoize, a kind of compiler-blessed built-in memoization for pure functuions allows pure memoization... (It is a trusted pure, where the trusted entity is the compiler)One could even argue that memoize makes impure functions pure because as long as you don't clear the cache, memoize!func is guarantied to return the same value for the same arguments.
Jan 04 2011
On 1/4/11 12:27 PM, Nick Sabalausky wrote:"Michel Fortin"<michel.fortin michelf.com> wrote in message news:ifvo9g$2d53$1 digitalmars.com...Even before that. There may be a visible difference between the first invocation and all others. AndreiOn 2011-01-04 12:53:43 -0500, bearophile<bearophileHUGS lycos.com> said:...until maxSize is exceeded and the cache is cleared.Regarding the purity of memoize, a kind of compiler-blessed built-in memoization for pure functuions allows pure memoization... (It is a trusted pure, where the trusted entity is the compiler)One could even argue that memoize makes impure functions pure because as long as you don't clear the cache, memoize!func is guarantied to return the same value for the same arguments.
Jan 04 2011
Nick Sabalausky:...until maxSize is exceeded and the cache is cleared.Clearing the whole cache when it's full is a brutal behaviour, I don't like it. See here for something better: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=126059 Bye, bearophile
Jan 04 2011
On 1/4/11 12:54 PM, bearophile wrote:Nick Sabalausky:There is a difference between caching and memoization. I wouldn't want to burden the memoization implementation with LRU management. Andrei...until maxSize is exceeded and the cache is cleared.Clearing the whole cache when it's full is a brutal behaviour, I don't like it. See here for something better: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=126059 Bye, bearophile
Jan 04 2011
Andrei:There is a difference between caching and memoization.Yep, memoization is a special case of caching: http://en.wikipedia.org/wiki/Memoization: "Although related to caching, memoization refers to a specific case of this optimization,"I wouldn't want to burden the memoization implementation with LRU management.My second proposal was a LRU, but the first simpler one wasn't. Even removing one random item when the cache memory is full (like the first inserted, so it's not a true LRU and you don't need double links) is better than clearing the clearing the whole memory as now. Bye, bearophile
Jan 04 2011
On 1/4/11 1:29 PM, bearophile wrote:Andrei:Removing a random element behaves surprisingly well and crossed my mind too. If only the built-in hash would allow such a thing... AndreiThere is a difference between caching and memoization.Yep, memoization is a special case of caching: http://en.wikipedia.org/wiki/Memoization: "Although related to caching, memoization refers to a specific case of this optimization,"I wouldn't want to burden the memoization implementation with LRU management.My second proposal was a LRU, but the first simpler one wasn't. Even removing one random item when the cache memory is full (like the first inserted, so it's not a true LRU and you don't need double links) is better than clearing the clearing the whole memory as now. Bye, bearophile
Jan 04 2011
On 2011-01-04 13:27:59 -0500, "Nick Sabalausky" <a a.a> said:"Michel Fortin" <michel.fortin michelf.com> wrote in message news:ifvo9g$2d53$1 digitalmars.com...Right, of course. I was speaking about the case where there is no maximum size (or maxSize == uint.max). I'm not saying it's a good idea in general, just that it has an interesting property. -- Michel Fortin michel.fortin michelf.com http://michelf.com/On 2011-01-04 12:53:43 -0500, bearophile <bearophileHUGS lycos.com> said:...until maxSize is exceeded and the cache is cleared.Regarding the purity of memoize, a kind of compiler-blessed built-in memoization for pure functuions allows pure memoization... (It is a trusted pure, where the trusted entity is the compiler)One could even argue that memoize makes impure functions pure because as long as you don't clear the cache, memoize!func is guarantied to return the same value for the same arguments.
Jan 04 2011
On Tuesday, January 04, 2011 06:15:16 Andrei Alexandrescu wrote:On 1/4/11 7:59 AM, Simen kjaeraas wrote:We really do need something like this, and actually I was thinking of bringing it up again. The compiler should be able to determine if a template function is pure on its own, since it can look at all of the functions called and determine whether they're pure or not (the non-template functions will be obvious and the template ones would require the templates to be instantiated anyway, which would tell the compiler whether they were pure or not if the compiler were determining that, so determining whether a particular instantiation of template function can be pure shouldn't be all that hard) and then make the template function pure if they are and impure if they're not. Having a condition for purity as you suggest might be better. I don't know. It essentially requires the same thing though. It's just that the programmer is then making it explicit. Regardless, we need _something_ like this or most template functions won't be able to be pure. And given how heavy templates are likely to be used in D - especially in Phobos - that seems like it would be unacceptable. Also, nothrow and const have the same problem (and immutable too, I suppose, though I'm not sure that that's all that big a deal since it's likely going to be rare to mark a function as immutable). So, really, we need to find a way to do this for all such attributes and qualifiers, not just pure. Maybe pure(), const(), nothrow(), and immutable() should all be introduced as you're suggesting with pure. It might be nice though to have the compiler be able to do it for you rather than having to list every template function manually, since presumably, you're going to have to list them all anyway, though perhaps pure() and its compatriots would be useful with conditions other than function purity. So, maybe something like pure(auto) should be used, and then that would effectively lower to pure() with isPure!func for every template function called by that template function. - Jonathan M DavisGuilherme Vieira <n2.nitrogen gmail.com> wrote:I've been mulling over this for a while, maybe it's time to start discussing it. Often you want to say "this entity is pure/const/immutable/safe... if this other entity is the same, or generally if this Boolean is true". So I was thinking of introducing e.g. a constrained pure: template memoize( alias fn ) { pure(isPure!fn) auto memoize( ParameterTypeTuple!fn ) { ... } } So generally when you write "attribute(expression)" the attribute will be in effect if and only if the expression is true. D has become very powerful at introspecting most of its own abstractions in the form of compile-time Booleans. This language extension would close the circle by allowing D to introduce attributes and qualifiers depending on compile-time Booleans.Walter: would it be hard/impossible for the compiler to look at memoize and tell it exhibits pure behavior and is, thus, pure?The simplest solution is this: template memoize( alias fn ) { static if ( isPure!fn ) { pure auto memoize( ParameterTypeTuple!fn ) { // Blah! } } else { auto memoize( ParameterTypeTuple!fn ) { // Blah! } } }
Jan 04 2011
Andrei:I just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here:I am not sure it's a good idea to put inside the D docs a link to the Google PDF visualizer. I suggest to remove the fact() example, because given the other fib() example, it adds nothing. Or if you want to keep it, you may differentiate it, showing something different, like putting the alias outside (and renaming the function).The maxSize parameter is a cutoff for the cache size. If upon a miss the length of the hash table is found to be maxSize, the table is simply cleared.<Two alternatives I'd like better: 1) The simpler alternative: when maxSize!=uint.max the associative array values are linked in a singly linked list. When there's a cache miss and aa.length > maxSize then the last item of the linked list is removed (there is a pointer to the last item that gets updated to the penultimate). 2) A bit more complex: when maxSize!=uint.max the associative array values are linked in a singly linked list. If the requested key is present, its value moves to the head of the doubly linked list. If it's not present, it's added at the head of the linked list. If aa.length > maxSize then the last item of the linked list is removed (a simple LRU implementation). A Python version: http://code.activestate.com/recipes/498110-memoize-decorator-with-o1-length-limited-lru-cache/ Of course I have written a memoize myself: http://code.activestate.com/recipes/466320-another-memoize/ Python has function decorators, so there's no need to change the name of memoized functions nor to change the function contents. Bye, bearophile
Jan 04 2011
On 1/3/2011 11:15 PM, Andrei Alexandrescu wrote:I just added a higher-order function memoize to std.functional which I think is pretty cool. See the docs here: http://d-programming-language.org/cutting-edge/phobos/std_functional.html#memoize I'm also thinking of adding that cutting-edge directory as a place for storing documentation for commits that are in flux but not officially released yet. AndreiIf it's helpful to anyone, Yage has a similar D1 function called Cache: http://dsource.org/projects/yage/browser/trunk/src/yage/core/cache.d
Jan 04 2011
If it's helpful to anyone, Yage has a similar D1 function called Cache: http://dsource.org/projects/yage/browser/trunk/src/yage/core/cache.dI almost forgot about licensing. I release this module under public domain or Boost 1.0, whichever is preferred.
Jan 04 2011