March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177


Kenji Hara <k.hara.pg@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |pull


--- Comment #10 from Kenji Hara <k.hara.pg@gmail.com> 2013-03-21 03:58:04 PDT ---
https://github.com/D-Programming-Language/dmd/pull/1779

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177


monarchdodra@gmail.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |monarchdodra@gmail.com


--- Comment #11 from monarchdodra@gmail.com 2013-03-21 06:18:01 PDT ---
(In reply to comment #10)
> https://github.com/D-Programming-Language/dmd/pull/1779

I'm a bit on the fence about this. I don't think it's the compiler's job to translate $ into length. length is not a magic word, it is just a range primitive. The compiler should have no knowledge about either of these. Further more, I think it could be dangerous to forward $ to length for any indiscriminate type.

Couldn't we implement this instead as a library solution, inside std.range? For example, std.array provides (pop)[front|back] for arrays. This is library, not compiler.

We could have std.range provide an global opDollar function that returns it's range argument's length instead? Simply:

//--------
import std.stdio;
import std.range;

auto opDollar(R)(auto ref R r)
    if (isInputRange!R && hasLength!R)
{
    return r.length;
}

struct S
{
    @property
    {
        enum empty = false;
        int front(){return 1;}
        size_t length(){return 10;}
    }
    void popFront(){}
    int opIndex(size_t i){return i;}
}

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

//--------

Idea being that if you call:
R r;
r[$ - 1];

Then: Either R defines opDollar, and everyone is happy. If not, and if range is imported, the the compiler "sees" an available std.range.opDollar function, and uses that instead.

That doesn't work right now (it just says "undefined identifier __dollar"), so it would still require a bit of compiler improvement, but I think it would be a better direction to take.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177



--- Comment #12 from Jonathan M Davis <jmdavisProg@gmx.com> 2013-03-21 07:05:45 PDT ---
If you have a type which defines length and has slicing or indexing, does it make any sense whatsoever for length _not_ to be the same as opDollar? I'd strongly argue that any type (be it a range or otherwise) which had indexing or slicing and had length and made opDollar something else other than length (or length something else than one past the last index) would be very badly designed.

If we don't make it so that length automatically aliases to opDollar if opDollar isn't already defined, then almost every single finite random-access range ever will have to manually alias length to opDollar, and we'll never be able to require that ranges with slicing define opDollar, because it would break too much code if we did.

I see no downside to this and huge problems if we don't do this. This is the key to being able to rely on opDollar existing for ranges types that should define it.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177



--- Comment #13 from monarchdodra@gmail.com 2013-03-21 07:18:13 PDT ---
(In reply to comment #12)
> If we don't make it so that length automatically aliases to opDollar if opDollar isn't already defined, then almost every single finite random-access range ever will have to manually alias length to opDollar, and we'll never be able to require that ranges with slicing define opDollar, because it would break too much code if we did.
> 
> I see no downside to this and huge problems if we don't do this. This is the key to being able to rely on opDollar existing for ranges types that should define it.

They point of dissension here (as I see it), is that you are saying that a range must define opDollar one way or another, whereas I think all we need is for r[$ - 1]/r[0 .. $] to work. My proposal would not prevent isSliceable for checking this, any less than isInputRange says that slices are ranges.

Slices have front defined externally, which allows us to use them as ranges all over the place.

I don't see why we can't use the same approach with ranges and opDollar.

I agree it is a HUGE boost to be able to rely on "r[0 .. $]" being legal. I just find it is kind of iffy to rely on the compiler for that...

But just to be clear, I prefer this than no solution.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177



--- Comment #14 from Steven Schveighoffer <schveiguy@yahoo.com> 2013-03-21 07:30:56 PDT ---
(In reply to comment #12)
> If you have a type which defines length and has slicing or indexing, does it make any sense whatsoever for length _not_ to be the same as opDollar? I'd strongly argue that any type (be it a range or otherwise) which had indexing or slicing and had length and made opDollar something else other than length (or length something else than one past the last index) would be very badly designed.

Yes, it makes sense in some cases for opDollar not to be length.

In the case of dcollections, I want opDollar to map to .end() (which is the
last cursor), not .length.  Please don't make $ map to .length, it will break
current dcollections.

Example:

I allow slicing based on index for TreeMap, because indexes are in order.  This code, would potentially compile, but return the unexpected slice of half the elements in the TreeMap:

auto m = new TreeMap!(int, int);
m[0] = 0;
m[2] = 2;
m[4] = 4;
m[6] = 6;
assert(m.length == 4);
auto sl = m[0..$]; // translates to m[0..m.length];
assert(sl.walkLength() == 2); // only 2 elements!

What I would like is for $ to map to m.end().

Another example is int[int]:

int[int] x;
x[1] = 14;
x[100] = 42;
assert(x[$-1] == 14);

if $ maps to x.length, then it happens to coincide with x[1], but that isn't necessarily the "last" element in the AA (which is how it reads).  In fact, I would argue x[$-1] shouldn't compile, it's value is meaningless.

Sorry that it makes you have to add boilerplate, but I think it's incorrect to always equate length with $.

The boilerplate isn't actually that bad, it's just an alias.  In generated code it adds NO bloat.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177



--- Comment #15 from Kenji Hara <k.hara.pg@gmail.com> 2013-03-21 07:50:48 PDT ---
(In reply to comment #14)
> Another example is int[int]:
> 
> int[int] x;
> x[1] = 14;
> x[100] = 42;
> assert(x[$-1] == 14);

That's a strong case. If compiler tries to translate $ to length automatically, library AA implementation will become impossible.

I'll close my pull request.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177



--- Comment #16 from Kenji Hara <k.hara.pg@gmail.com> 2013-03-21 07:59:47 PDT ---
The idea in comment #11 is interesting, but one problem point is that current compiler does not see UFCS fallback for operator overloading.

If we accept it, should we also accept this?

string opBinary(string op)(string s1, string s2) if (op == "+") {
    return s1 ~ s2;
}
void main() {
    assert("hello " + "world!" == "hello world!");
    // --> "hello ".opBinary!"+"("world!")    // op-overloading
    // --> .opBinary!"+"("hello ", "world!")  // UFCS
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177


Steven Schveighoffer <schveiguy@yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |schveiguy@yahoo.com


--- Comment #17 from Steven Schveighoffer <schveiguy@yahoo.com> 2013-03-21 08:24:32 PDT ---
(In reply to comment #16)
> The idea in comment #11 is interesting, but one problem point is that current compiler does not see UFCS fallback for operator overloading.

That is a deficiency that is easily worked around:

struct Concatable
{
   private string str;
   this(string str) {this.str = str;}
   string opBinary(string op)(string other) if (op == "+") {return str ~
other;}
}

Concatable c(string s) { return Concatable(s);}

auto s = "hello".c + "world";


> If we accept it, should we also accept this?
> 
> string opBinary(string op)(string s1, string s2) if (op == "+") {
>     return s1 ~ s2;
> }

Should we accept that this is now possible?  Sure, I don't see why not, it is quite possible without additions to the language to make that a reality with a tiny insignificant annoyance.  Should we accept string + string as a feature of D/phobos?  No.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177



--- Comment #18 from Jonathan M Davis <jmdavisProg@gmx.com> 2013-03-21 08:27:16 PDT ---
> That's a strong case. If compiler tries to translate $ to length automatically, library AA implementation will become impossible.

That's not a problem at all. As long as the logic is that length is aliased to opDollar as long as opDollar isn't defined, then it won't break anything. Anything that needs to define opDollar differently will define opDollar, and for the rest, if they define length, then it gets aliased to opDollar. I really don't see the problem with that. And without this, requiring opDollar with hasSlicing will break tons of code.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 21, 2013
http://d.puremagic.com/issues/show_bug.cgi?id=7177



--- Comment #19 from monarchdodra@gmail.com 2013-03-21 08:32:44 PDT ---
(In reply to comment #14)
> Yes, it makes sense in some cases for opDollar not to be length.
>
> [SNIP]

Note though that opDollar would *fallback* to length if you did not implement opDollar. You are still free to implement your own opDollar if you wish, so your DCollections would still be fine.

(In reply to comment #15)
> (In reply to comment #14)
> > Another example is int[int]:
> > 
> > int[int] x;
> > x[1] = 14;
> > x[100] = 42;
> > assert(x[$-1] == 14);
> 
> That's a strong case. If compiler tries to translate $ to length automatically, library AA implementation will become impossible.
> 
> I'll close my pull request.

No need! Please don't close it quite yet. A simple "@disable opDollar();" or
"private opDollar();" should shut down this problem.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------