December 09, 2007
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
December 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:
> 
> // 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

It's not just opApply; any function would need to be written three times for accessing constant data in a possibly mutable class.
December 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?)
December 09, 2007
Janice Caron wrote:
>     invarant C = new C;
> 
> will fail to compile, and there is no "inew".

invariant C = cast(invariant) new C();
December 09, 2007
On 12/9/07, Robert Fraser <fraserofthenight@gmail.com> wrote:
> Janice Caron wrote:
> >     invarant C = new C;
> >
> > will fail to compile, and there is no "inew".
>
> invariant C = cast(invariant) new C();

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!
December 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.
> 
> --bb

There'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);
}
December 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);
}
December 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?
> 
> 	-- Daniel

In 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);
> }
December 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...
December 10, 2007

Bill Baxter wrote:
> 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...

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.  :)

	-- Daniel
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home