www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - What's the deal with __buck?

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
I stumbled upon a very interesting problem. Consider an infinite range 
that generates the numbers 1, 2, 3, ...

That range doesn't have a "length" member. However, it is a random 
access range, which makes things rather interesting. Now consider I want 
to advance 10 steps in that range. Being an obedient D programmer I'd write:

auto r = iota(1);
// skip 10 steps
r = r[10 .. $];

Now this is very cool. If a range is infinite, I can use the $ symbol in 
the right position, but nothing else. So I tried to effect that and got 
the following to compile:

struct DollarType {}
enum DollarType __dollar = DollarType();

struct S
{
     void opSlice(uint, DollarType)
     {
     }
}

void main()
{
     S s;
     s[0 .. $];
}

This is cool because it allows detection of passing $. Now the problem 
is, I can't seem to get rid of the __dollar definition! Has anyone found 
a general-enough trick? I'd want a[...$...] to morph the $ into a.length 
*iff* a defines length, and go with the __dollar otherwise.


Thanks,

Andrei
Jan 28 2009
next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Thu, Jan 29, 2009 at 1:37 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 I stumbled upon a very interesting problem. Consider an infinite range that
 generates the numbers 1, 2, 3, ...

 That range doesn't have a "length" member. However, it is a random access
 range, which makes things rather interesting. Now consider I want to advance
 10 steps in that range. Being an obedient D programmer I'd write:

 auto r = iota(1);
 // skip 10 steps
 r = r[10 .. $];

 Now this is very cool. If a range is infinite, I can use the $ symbol in the
 right position, but nothing else. So I tried to effect that and got the
 following to compile:

 struct DollarType {}
 enum DollarType __dollar = DollarType();

 struct S
 {
    void opSlice(uint, DollarType)
    {
    }
 }

 void main()
 {
    S s;
    s[0 .. $];
 }

 This is cool because it allows detection of passing $. Now the problem is, I
 can't seem to get rid of the __dollar definition! Has anyone found a
 general-enough trick? I'd want a[...$...] to morph the $ into a.length *iff*
 a defines length, and go with the __dollar otherwise.

You need to give your DollarType an int and some arithmetic operators and have it represent an /offset/ from the end rather than signifying the end itself. Otherwise a[$-1] can't work. Once you do that, then you can do things like struct S { T opSlice(uint a, DollarType b) { return opSlice(a, length+b.offset); } T opSlice(uint a, uint b) { .... } ... } --bb
Jan 28 2009
prev sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Thu, Jan 29, 2009 at 1:48 PM, Bill Baxter <wbaxter gmail.com> wrote:
 On Thu, Jan 29, 2009 at 1:37 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 I stumbled upon a very interesting problem. Consider an infinite range that
 generates the numbers 1, 2, 3, ...

 That range doesn't have a "length" member. However, it is a random access
 range, which makes things rather interesting. Now consider I want to advance
 10 steps in that range. Being an obedient D programmer I'd write:

 auto r = iota(1);
 // skip 10 steps
 r = r[10 .. $];

 Now this is very cool. If a range is infinite, I can use the $ symbol in the
 right position, but nothing else. So I tried to effect that and got the
 following to compile:

 struct DollarType {}
 enum DollarType __dollar = DollarType();

 struct S
 {
    void opSlice(uint, DollarType)
    {
    }
 }

 void main()
 {
    S s;
    s[0 .. $];
 }

 This is cool because it allows detection of passing $. Now the problem is, I
 can't seem to get rid of the __dollar definition! Has anyone found a
 general-enough trick? I'd want a[...$...] to morph the $ into a.length *iff*
 a defines length, and go with the __dollar otherwise.

You need to give your DollarType an int and some arithmetic operators and have it represent an /offset/ from the end rather than signifying the end itself. Otherwise a[$-1] can't work. Once you do that, then you can do things like struct S { T opSlice(uint a, DollarType b) { return opSlice(a, length+b.offset); } T opSlice(uint a, uint b) { .... } ... }

Nevermind, it just sunk in what you meant. I think it's only a problem when you want to have some things in a module with special __dollar and some with not? Or I suppose you're probably trying to make template things which may or may not be infinite? I guess what's required is the ability to define the __dollar in the scope of a class/struct/template. --bb
Jan 28 2009
next sibling parent reply BCS <none anon.com> writes:
Hello Bill,
 
 I guess what's required is the ability to define the __dollar in the
 scope of a class/struct/template.

I think that is the correct solution.
 
 --bb
 

Jan 28 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
BCS wrote:
 Hello Bill,
 I guess what's required is the ability to define the __dollar in the
 scope of a class/struct/template.

I think that is the correct solution.

Ok, thanks to all. So __dollar is looked up at module scope for anything that's not a built-in array. I thought some more about it and I think there's no need for __dollar. It's enough that: a) In slice expressions and index expressions with one argument, $ expands to value.length, where value is the object being indexed/sliced (if an unnamed temporary, the value is of course only evaluated once) b) In index expressions with multiple arguments, $ expand to value.length(i), where i is the zero-based argument position. This is exactly enough what's needed to make it all work. Infinite ranges may define a symbolic infinite length and overload slicing on it. Andrei Andrei
Jan 29 2009
next sibling parent reply Don <nospam nospam.com> writes:
Jarrett Billingsley wrote:
 On Thu, Jan 29, 2009 at 12:48 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 BCS wrote:
 Hello Bill,
 I guess what's required is the ability to define the __dollar in the
 scope of a class/struct/template.


that's not a built-in array. I thought some more about it and I think there's no need for __dollar. It's enough that: a) In slice expressions and index expressions with one argument, $ expands to value.length, where value is the object being indexed/sliced (if an unnamed temporary, the value is of course only evaluated once) b) In index expressions with multiple arguments, $ expand to value.length(i), where i is the zero-based argument position. This is exactly enough what's needed to make it all work. Infinite ranges may define a symbolic infinite length and overload slicing on it.

Sounds good. Question - since length will work for multiple indices, will it ever be possible to have .. multiple slices? a[x1 .. x2, y1 .. y2] = b;

You can do that with opIndex already.
Jan 29 2009
parent Don <nospam nospam.com> writes:
Bill Baxter wrote:
 On Fri, Jan 30, 2009 at 5:58 AM, Don <nospam nospam.com> wrote:
 Jarrett Billingsley wrote:
 On Thu, Jan 29, 2009 at 12:48 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 BCS wrote:
 Hello Bill,
 I guess what's required is the ability to define the __dollar in the
 scope of a class/struct/template.


that's not a built-in array. I thought some more about it and I think there's no need for __dollar. It's enough that: a) In slice expressions and index expressions with one argument, $ expands to value.length, where value is the object being indexed/sliced (if an unnamed temporary, the value is of course only evaluated once) b) In index expressions with multiple arguments, $ expand to value.length(i), where i is the zero-based argument position. This is exactly enough what's needed to make it all work. Infinite ranges may define a symbolic infinite length and overload slicing on it.

will it ever be possible to have .. multiple slices? a[x1 .. x2, y1 .. y2] = b;


Can you? How? As far as I know any index expression with more than one .. in it automatically generates a compiler error.

Aargh, you're right. I remembered that I'd got it to generate an AST for me, but I forgot that I had put in a hack for ..
Jan 29 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jarrett Billingsley wrote:
 On Thu, Jan 29, 2009 at 12:48 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 BCS wrote:
 Hello Bill,
 I guess what's required is the ability to define the __dollar in the
 scope of a class/struct/template.


that's not a built-in array. I thought some more about it and I think there's no need for __dollar. It's enough that: a) In slice expressions and index expressions with one argument, $ expands to value.length, where value is the object being indexed/sliced (if an unnamed temporary, the value is of course only evaluated once) b) In index expressions with multiple arguments, $ expand to value.length(i), where i is the zero-based argument position. This is exactly enough what's needed to make it all work. Infinite ranges may define a symbolic infinite length and overload slicing on it.

Sounds good. Question - since length will work for multiple indices, will it ever be possible to have .. multiple slices? a[x1 .. x2, y1 .. y2] = b;

That's rather exotic. After that the road is opened for free-form combinations of a .. b and a. But I can definitely see some good uses for it. Andrei
Jan 29 2009
parent Don <nospam nospam.com> writes:
Jarrett Billingsley wrote:
 On Thu, Jan 29, 2009 at 6:00 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 a[x1 .. x2, y1 .. y2] = b;

combinations of a .. b and a. But I can definitely see some good uses for it.

Hm.. if you _wanted_ to interleave slices and single indices, how would you do it? How about we take a page from Python's book: it doesn't (used to but no longer) distinguish between slicing and indexing. It uses the same "operator overload" for both, but slicing passes a 2-tuple as the "index", kind of like: blah opIndex(int x) // used for a[0] blarg opIndex(Slice!(int, int) x) // used for a[0 .. 1] Assuming we have a "struct Slice(Lo, Hi) { Lo lo; Hi hi; }" type. Then, you could just have opIndex[Assign] take multiple parameters - like it does now. And of course, it could just take a tuple and figure out what to do based on whether each thing is an int or if it's a slice. Complex, but completely doable, and it still leaves simple cases simple.

That's exactly what I did in Blade. opSlice() is a dead end. The algebra for combining consecutive slices is quite interesting, but it's not really that complicated. You can always transform any expression like: x[3..5, 6, 2..$][4, $][5..$][3..7][6]; into a single mixed opIndex() call. You can do mixed index and slices either by making every slice a tuple, or by having a bool array stating whether each entry is an index, or just the second part of a slice. (This way every parameter in opIndex is the same type: there's less template bloat).
Jan 30 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Thu, Jan 29, 2009 at 12:48 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 BCS wrote:
 Hello Bill,
 I guess what's required is the ability to define the __dollar in the
 scope of a class/struct/template.

I think that is the correct solution.

Ok, thanks to all. So __dollar is looked up at module scope for anything that's not a built-in array. I thought some more about it and I think there's no need for __dollar. It's enough that: a) In slice expressions and index expressions with one argument, $ expands to value.length, where value is the object being indexed/sliced (if an unnamed temporary, the value is of course only evaluated once) b) In index expressions with multiple arguments, $ expand to value.length(i), where i is the zero-based argument position. This is exactly enough what's needed to make it all work. Infinite ranges may define a symbolic infinite length and overload slicing on it.

Sounds good. Question - since length will work for multiple indices, will it ever be possible to have .. multiple slices? a[x1 .. x2, y1 .. y2] = b;
Jan 29 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Fri, Jan 30, 2009 at 5:58 AM, Don <nospam nospam.com> wrote:
 Jarrett Billingsley wrote:
 On Thu, Jan 29, 2009 at 12:48 PM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 BCS wrote:
 Hello Bill,
 I guess what's required is the ability to define the __dollar in the
 scope of a class/struct/template.

I think that is the correct solution.

Ok, thanks to all. So __dollar is looked up at module scope for anything that's not a built-in array. I thought some more about it and I think there's no need for __dollar. It's enough that: a) In slice expressions and index expressions with one argument, $ expands to value.length, where value is the object being indexed/sliced (if an unnamed temporary, the value is of course only evaluated once) b) In index expressions with multiple arguments, $ expand to value.length(i), where i is the zero-based argument position. This is exactly enough what's needed to make it all work. Infinite ranges may define a symbolic infinite length and overload slicing on it.

Sounds good. Question - since length will work for multiple indices, will it ever be possible to have .. multiple slices? a[x1 .. x2, y1 .. y2] = b;

You can do that with opIndex already.

Can you? How? As far as I know any index expression with more than one .. in it automatically generates a compiler error. --bb
Jan 29 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Fri, Jan 30, 2009 at 8:00 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Jarrett Billingsley wrote:

 Sounds good.  Question - since length will work for multiple indices,
 will it ever be possible to have .. multiple slices?

 a[x1 .. x2, y1 .. y2] = b;

That's rather exotic. After that the road is opened for free-form combinations of a .. b and a. But I can definitely see some good uses for it.

Multi-dim slicing is bread and butter for array math packages like Matlab or NumPy. --bb
Jan 29 2009
prev sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Thu, Jan 29, 2009 at 6:00 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 a[x1 .. x2, y1 .. y2] = b;

That's rather exotic. After that the road is opened for free-form combinations of a .. b and a. But I can definitely see some good uses for it.

Hm.. if you _wanted_ to interleave slices and single indices, how would you do it? How about we take a page from Python's book: it doesn't (used to but no longer) distinguish between slicing and indexing. It uses the same "operator overload" for both, but slicing passes a 2-tuple as the "index", kind of like: blah opIndex(int x) // used for a[0] blarg opIndex(Slice!(int, int) x) // used for a[0 .. 1] Assuming we have a "struct Slice(Lo, Hi) { Lo lo; Hi hi; }" type. Then, you could just have opIndex[Assign] take multiple parameters - like it does now. And of course, it could just take a tuple and figure out what to do based on whether each thing is an int or if it's a slice. Complex, but completely doable, and it still leaves simple cases simple.
Jan 29 2009