digitalmars.D - opApply and const
- Bill Baxter (26/26) Dec 08 2007 I mentioned it in another thread but I just wanted to confirm it.
- Robert Fraser (3/36) Dec 09 2007 It's not just opApply; any function would need to be written three times...
- Janice Caron (9/10) Dec 09 2007 And "ref const T" seems a bit of an oxymoron anyway.
- Robert Fraser (2/5) Dec 09 2007 invariant C = cast(invariant) new C();
- Janice Caron (5/10) Dec 09 2007 I didn't forget that, I just didn't see the point of it.
- Daniel Keep (53/67) Dec 09 2007 There's really no need for the counter versions. Just solve this the
- Daniel Keep (71/71) Dec 09 2007 My apologies. It *does* actually support arbitrary opApply()s. What
- Christopher Wright (5/92) Dec 09 2007 In the general case: no.
- Bill Baxter (6/9) Dec 09 2007 I like it (no surprise since I also like Python).
- Daniel Keep (15/25) Dec 09 2007 Well, this is how Python does it, I believe. The position there appears
- Steven Schveighoffer (20/24) Dec 10 2007 Note that there is no requirement to implement the invariant versions. ...
I mentioned it in another thread but I just wanted to confirm it. Am I right in thinking that if we want to have user types act like built-in containers w.r.t iteration in D2 then we need to implement 12 different versions of opApply?! Namely: // plain vanilla int opApply(int delegate(ref T)) {...} const int opApply(int delegate(ref const T)) {...} invariant int opApply(int delegate(ref invariant T)) {...} // with counter int opApply(int delegate(ref size_t, ref T)) {...} const int opApply(int delegate(ref size_t, ref const T)) {...} invariant int opApply(int delegate(ref size_t, ref invariant T)) {...} // backwards int opApplyReverse(int delegate(ref T)) {...} const int opApplyReverse(int delegate(ref const T)) {...} invariant int opApplyReverse(int delegate(ref invariant T)) {...} // backwards with counter int opApplyReverse(int delegate(ref size_t, ref T)) {...} const int opApplyReverse(int delegate(ref size_t, ref const T)) {...} invariant int opApplyReverse(int delegate(ref size_t, ref invariant T)) {...} That's a lot of opApply! I was already mildly annoyed by the four needed without const. But with both const (and invariant) it just seems downright silly. --bb
Dec 08 2007
Bill Baxter wrote:I mentioned it in another thread but I just wanted to confirm it. Am I right in thinking that if we want to have user types act like built-in containers w.r.t iteration in D2 then we need to implement 12 different versions of opApply?! Namely: // plain vanilla int opApply(int delegate(ref T)) {...} const int opApply(int delegate(ref const T)) {...} invariant int opApply(int delegate(ref invariant T)) {...} // with counter int opApply(int delegate(ref size_t, ref T)) {...} const int opApply(int delegate(ref size_t, ref const T)) {...} invariant int opApply(int delegate(ref size_t, ref invariant T)) {...} // backwards int opApplyReverse(int delegate(ref T)) {...} const int opApplyReverse(int delegate(ref const T)) {...} invariant int opApplyReverse(int delegate(ref invariant T)) {...} // backwards with counter int opApplyReverse(int delegate(ref size_t, ref T)) {...} const int opApplyReverse(int delegate(ref size_t, ref const T)) {...} invariant int opApplyReverse(int delegate(ref size_t, ref invariant T)) {...} That's a lot of opApply! I was already mildly annoyed by the four needed without const. But with both const (and invariant) it just seems downright silly. --bbIt's not just opApply; any function would need to be written three times for accessing constant data in a possibly mutable class.
Dec 09 2007
On 12/9/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:const int opApply(int delegate(ref const T)) {...}And "ref const T" seems a bit of an oxymoron anyway. I can bring it down to eight. You shouldn't have to deal with invariant classes. These can't really exist in D, since invarant C = new C; will fail to compile, and there is no "inew". But yeah - eight versions of the same function is too much. Is there any way we can narrow it down to one? (One that actually works, in all eight cases?)
Dec 09 2007
Janice Caron wrote:invarant C = new C; will fail to compile, and there is no "inew".invariant C = cast(invariant) new C();
Dec 09 2007
On 12/9/07, Robert Fraser <fraserofthenight gmail.com> wrote:Janice Caron wrote:I didn't forget that, I just didn't see the point of it. What I did forget, though, is invariant structs. Now they do make sense! And surely opApply has to work for those, too. So we're back up to twelve again!invarant C = new C; will fail to compile, and there is no "inew".invariant C = cast(invariant) new C();
Dec 09 2007
Bill Baxter wrote:I mentioned it in another thread but I just wanted to confirm it. Am I right in thinking that if we want to have user types act like built-in containers w.r.t iteration in D2 then we need to implement 12 different versions of opApply?! Namely: [snip] That's a lot of opApply! I was already mildly annoyed by the four needed without const. But with both const (and invariant) it just seems downright silly. --bbThere's really no need for the counter versions. Just solve this the same way Python did: with a wrapper. Admittedly, this doesn't seem to work for more than one argument (foreach appears to dislike tuples for arguments), but it illustrates the general concept. -- Daniel module enumerate; import std.stdio; import std.traits; struct Enumerate(T) { private { T source; alias ParameterTypeTuple!(typeof(T.opApply))[0] opApply_arg; alias ParameterTypeTuple!(opApply_arg) opApply_type; } int opApply(int delegate(ref size_t, opApply_type) dg) { int result = 0; size_t i = 0; foreach( x ; source ) { if( (result=dg(i,x)) != 0 ) break; ++i; } return result; } } Enumerate!(T) enumerate(T)(T source) { return Enumerate!(T)(source); } class Foo { static const words = ["On"[],"a","Sunday,","riding","my","bike"]; int opApply(int delegate(ref char[]) dg) { int result = 0; foreach( word ; words ) if( (result=dg(word)) != 0 ) break; return result; } } void main() { foreach( word ; new Foo ) writef("%s ",word); writefln(""); writefln(""); foreach( i,word ; enumerate(new Foo) ) writefln("[%s] %s", i, word); }
Dec 09 2007
My apologies. It *does* actually support arbitrary opApply()s. What probably *doesn't* support are types with more than one opApply. Can we get a list of overloads with D 2.0 yet? -- Daniel module enumerate; import std.stdio; import std.traits; struct Enumerate(T) { private { T source; alias ParameterTypeTuple!(typeof(T.opApply))[0] opApply_arg; alias ParameterTypeTuple!(opApply_arg) opApply_type; } int opApply(int delegate(ref size_t, opApply_type) dg) { int result = 0; size_t i = 0; foreach( ref opApply_type x ; source ) { if( (result=dg(i,x)) != 0 ) break; ++i; } return result; } } Enumerate!(T) enumerate(T)(T source) { return Enumerate!(T)(source); } class Foo { static const words = ["On"[],"a","Sunday,","riding","my","bike"]; int opApply(int delegate(ref char[]) dg) { int result = 0; foreach( word ; words ) if( (result=dg(word)) != 0 ) break; return result; } } class Bar { int opApply(int delegate(ref real, ref char[]) dg) { // Nasty code: real r; char[] d; r = 3.14159; d = "pi"; dg(r,d); r = 3.0; d = "pi (according to the Bible)"; dg(r,d); r = 42.0; d = "meaning of life"; dg(r,d); r = real.nan; d = "how much wood would a wood chuck chuck?"; dg(r,d); return 0; } } void main() { foreach( word ; new Foo ) writef("%s ",word); writefln(""); writefln(""); foreach( i,word ; enumerate(new Foo) ) writefln("[%s] %s", i, word); writefln(""); foreach( r,desc ; new Bar ) writefln("%s: %s", r, desc); writefln(""); foreach( i,r,desc ; enumerate(new Bar) ) writefln("[%s] %s: %s", i, r, desc); }
Dec 09 2007
Daniel Keep wrote:My apologies. It *does* actually support arbitrary opApply()s. What probably *doesn't* support are types with more than one opApply. Can we get a list of overloads with D 2.0 yet? -- DanielIn the general case: no. If you're only concerned about the virtual ones: yes, with __traits(getVirtualFunctions). (I think that's it; I always forget what it is exactly.)module enumerate; import std.stdio; import std.traits; struct Enumerate(T) { private { T source; alias ParameterTypeTuple!(typeof(T.opApply))[0] opApply_arg; alias ParameterTypeTuple!(opApply_arg) opApply_type; } int opApply(int delegate(ref size_t, opApply_type) dg) { int result = 0; size_t i = 0; foreach( ref opApply_type x ; source ) { if( (result=dg(i,x)) != 0 ) break; ++i; } return result; } } Enumerate!(T) enumerate(T)(T source) { return Enumerate!(T)(source); } class Foo { static const words = ["On"[],"a","Sunday,","riding","my","bike"]; int opApply(int delegate(ref char[]) dg) { int result = 0; foreach( word ; words ) if( (result=dg(word)) != 0 ) break; return result; } } class Bar { int opApply(int delegate(ref real, ref char[]) dg) { // Nasty code: real r; char[] d; r = 3.14159; d = "pi"; dg(r,d); r = 3.0; d = "pi (according to the Bible)"; dg(r,d); r = 42.0; d = "meaning of life"; dg(r,d); r = real.nan; d = "how much wood would a wood chuck chuck?"; dg(r,d); return 0; } } void main() { foreach( word ; new Foo ) writef("%s ",word); writefln(""); writefln(""); foreach( i,word ; enumerate(new Foo) ) writefln("[%s] %s", i, word); writefln(""); foreach( r,desc ; new Bar ) writefln("%s: %s", r, desc); writefln(""); foreach( i,r,desc ; enumerate(new Bar) ) writefln("[%s] %s: %s", i, r, desc); }
Dec 09 2007
Daniel Keep wrote:My apologies. It *does* actually support arbitrary opApply()s. What probably *doesn't* support are types with more than one opApply. Can we get a list of overloads with D 2.0 yet?I like it (no surprise since I also like Python). How about reverse? Can you cook up a reversed() template that would use an opApplyReverse if it exists or else forward iterate into a buffer and reverse iterate over that? Or maybe creating a big temp buffer silently isn't such a hot idea...
Dec 09 2007
Bill Baxter wrote:Daniel Keep wrote:Well, this is how Python does it, I believe. The position there appears to be: better to be possible and inefficient than not possible at all. Really, I think it's somewhat surprising that D doesn't have *any* iteration tools whatsoever. If I weren't already so busy with other things, I'd be tempted to write some myself. And there's always the chance that downs has written stuff already (that no one else can read without screaming in pain.) ;) Still, I think one of the biggest hurdles to writing a good iterator toolkit is how opApply is implemented in D. Because of the callback design, you can't do anything with resumable iterators, you can't do multiple iterations in lockstep... it's a bit of a pity that opApply is so firmly entrenched in the language. Oh well; I suppose there's always D 3.0. :) -- DanielMy apologies. It *does* actually support arbitrary opApply()s. What probably *doesn't* support are types with more than one opApply. Can we get a list of overloads with D 2.0 yet?I like it (no surprise since I also like Python). How about reverse? Can you cook up a reversed() template that would use an opApplyReverse if it exists or else forward iterate into a buffer and reverse iterate over that? Or maybe creating a big temp buffer silently isn't such a hot idea...
Dec 09 2007
"Bill Baxter" wroteI mentioned it in another thread but I just wanted to confirm it. Am I right in thinking that if we want to have user types act like built-in containers w.r.t iteration in D2 then we need to implement 12 different versions of opApply?!Note that there is no requirement to implement the invariant versions. An invariant object can be implicitly cast to a const object, and so the const versions can be called by invariant objects. The only benefit of having an invariant AND a const version is to skip things like mutex locking in the invariant version. I've been thinking about this for a while, and I've come to the conclusion that I think invariant and const functions are so damned similar, that there must be a way to specify them both at once. I mean, if the only difference between an invariant and a const is you don't have to do mutex locks to read data, can't the compiler just make 2 versions of a const function, and not do the synchronized(X) where X is invariant? It'd be like an automatic template, where any time you specify a const parameter (including 'this'), it could also generate a function where the const parameter is invariant. I know Walter wants to promote invariant functions as a way to help avoid thread mistakes, but I highly doubt I will be a common practice, especially by people who are not familiar with the perils of threading. This seems to me to be similar to inline, where the compiler would know better than me when it could optimize by calling an invariant version. -Steve
Dec 10 2007