October 11, 2018
On Wednesday, 10 October 2018 at 23:04:46 UTC, James Japherson wrote:

> The whole point is not to use $ as an identifier but to specify to the compiler of that it can rewrite it.

It's called 'alias'.

// compile time
int foo(alias index)(int[] a)
{
	return a[index(a.length)];
}

// run time
int barr(int[] a, size_t function(size_t) index)
{
	return a[index(a.length)];
}

int main()
{
	import std.range: iota;
	import std.array: array;
	import std.stdio: writeln;

	int[100] a = iota(0,100).array;

	a.foo!(l => l-3).writeln;
	a.barr(l => l-3).writeln;

	return 0;
}
October 11, 2018
On Wednesday, 10 October 2018 at 23:04:46 UTC, James Japherson wrote:
> The whole point is not to use $ as an identifier but to specify to the compiler of that it can rewrite it.

I know. I'm pointing out that as syntactic sugar, it can't be passed as an int.


> You seem to think that what the compiler does is absolute authority. This is why I said "It would be nice".... meaning that if we had some a feature(which is entirely doable, not some mathematical impossibility), it would allow one to express the limit of an index in a concise way. Your templated version is not concise. All you really proved is that the compiler can be given a rewrite rule and handle this nicely.
>
> $ is not an used for identifiers, it is used to specify that the maximum length of the array it is used in is to be used. It is short hand for doing hacks such as specifying -1 for maximum length, etc.
>
> You seem to to have not understood the problem.
>
> I mean, don't you understand that the entire point of $ in the first place is just syntactic sugar?

I do. Do you understand what syntactic sugar even is? Do you understand what an int is? Do you understand what a compiler is and does? Do you have any idea what separate compilation is?

Now, to spell it out: $ is syntactic sugar that is replaced with the .length or .opDollar property of the array/range used in the surrounding expression. It is valid in arr[$-1] exactly because arr is an array that provides the necessary context. The result of an expression using $ is generally a size_t, but can be any type when the range overloads opDollar. The behavior of $ as something you can pass around in other contexts is not easy to pin down.

Consider:

struct MyRange {
    string opDollar() { return "foo!"; }
    bool opIndex(size_t idx) { return false; }
    bool opIndex(string idx) { return true; }
}

bool fun(size_t idx) {
    MyRange mr;
    return mr[idx];
}

unittest {
    auto x = fun($); // What does it even mean?
}


> It also has no context in and of itself. The compiler knows what to do with it... The same can be done with function arguments. You just haven't thought about the problem enough.

Then please, show me how it works in my and others' examples.

The compiler only know what we tell it. Your description of this feature is woefully inadequate at providing the information necessary to tell the compiler how to do this.

Now, I and others may have come off as slightly... dismissive. It's not that we don't like your idea, we just see a lot of problems with it. There may be solutions to these, and if you have said solutions, we'd love to see them. You'll need to give a more technical description than you have thus far, though.

--
  Simen
October 11, 2018
On Thursday, 11 October 2018 at 06:58:08 UTC, Simen Kjærås wrote:
>
> unittest {
>     auto x = fun($); // What does it even mean?
> }

After some reading through the whole thread I think his "$ idea" can only be applied to a RandomAccessRange (and similar) where the size can be known...

October 11, 2018
On Thursday, 11 October 2018 at 00:01:27 UTC, James Japherson wrote:

> I don't understand why you need to be convinced that this is relevant.
>
> Do you not realize that there are cases where one wants to select the last element of a list without having to explicitly know it?

It's fine if you want to have a discussion about this feature, but if you're serious about changing the language, you will not only have to show that it is relevant (and important), but also that you can't do it with existing language features. I'm not being dismissive, just trying to save you some time by avoiding the same pointless discussion I've seen over and over again.
October 11, 2018
On 10/11/2018 04:36 AM, Dejan Lekic wrote:
> On Thursday, 11 October 2018 at 06:58:08 UTC, Simen Kjærås wrote:
>>
>> unittest {
>>     auto x = fun($); // What does it even mean?
>> }
> 
> After some reading through the whole thread I think his "$ idea" can only be applied to a RandomAccessRange (and similar) where the size can be known...

It already works with a RandomAccessRange:

void main()
{
    auto a = [1, 2, 3, 4];
    auto b = a.map!(x => x + 1);
    writeln(b[$-1]);
}

It's just that the compiler can't reach into the body of another function to determine what variable you're talking about to find the correct value to pass.
October 14, 2018
On Wednesday, 10 October 2018 at 13:32:15 UTC, Simen Kjærås wrote:
> struct Sentinel {}
> Sentinel $;
>
> void foo(T)(T loc) {
>     auto bar = double[RandomPInt+1];
>     static if (is(T == Sentinel)) {
>         return bar[$];
>     } else {
>         return bar[loc];
>     }
> }
>
> unittest {
>     foo($);
> }

It looks to me OP is proposing exactly this, but wants the subscript expression to support it.
That would require introducing a new type or changing size_t, so that both integer indices and the sentinel can be passed around.
Present-day size_t is unsigned, so we can't use negative values to represent it.

I guess it could be useful when you are manipulating multiple arrays at the same time in a functional fashion and need the upper bound of all of them at one point, which could be different.
October 14, 2018
On Sunday, 14 October 2018 at 13:18:37 UTC, lngns wrote:
> That would require introducing a new type

Or just use int with a negative number... That's how it's done in some dynamic languages.
But my point is that it should be compatible with pre-existing code using unsigned indices somehow. I don't think that is possible.
October 14, 2018
On Sunday, 14 October 2018 at 14:35:36 UTC, lngns wrote:
> On Sunday, 14 October 2018 at 13:18:37 UTC, lngns wrote:
>> That would require introducing a new type
>
> Or just use int with a negative number... That's how it's done in some dynamic languages.
> But my point is that it should be compatible with pre-existing code using unsigned indices somehow. I don't think that is possible.

Another way to do this with UFCS:

// write-once library wrapper
struct Indexer(R) {
    R r;
    auto opDollar() { return r.length; }
    auto opIndex(size_t i) { return r[i]; }
}
auto indexer(R)(R r) { return Indexer(r); }

// rewrite index parameter => return wrapped range
auto foo()
{
    auto arr = (...);
    return indexer(arr);
}

// easy-to-use result
auto end = foo[$-1];
October 16, 2018
On Sunday, 14 October 2018 at 15:27:07 UTC, Michael Coulombe wrote:
> On Sunday, 14 October 2018 at 14:35:36 UTC, lngns wrote:
>> On Sunday, 14 October 2018 at 13:18:37 UTC, lngns wrote:
>>> That would require introducing a new type
>>
>> Or just use int with a negative number... That's how it's done in some dynamic languages.
>> But my point is that it should be compatible with pre-existing code using unsigned indices somehow. I don't think that is possible.
>
> Another way to do this with UFCS:
>
> // write-once library wrapper
> struct Indexer(R) {
>     R r;
>     auto opDollar() { return r.length; }
>     auto opIndex(size_t i) { return r[i]; }
> }
> auto indexer(R)(R r) { return Indexer(r); }
>
> // rewrite index parameter => return wrapped range
> auto foo()
> {
>     auto arr = (...);
>     return indexer(arr);
> }
>
> // easy-to-use result
> auto end = foo[$-1];

Didn't feel like making a full expression tree thing, so feel free to extend this:

struct Index {
    struct Op {
        string op;
        int value;
        void apply(ref size_t val) {
            switch (op) {
                case "+": val += value; break;
                case "-": val -= value; break;
                case "*": val *= value; break;
                case "/": val /= value; break;
                case "%": val %= value; break;
                default: assert(false);
            }
        }
    }
    Op[] ops;
    bool fromEnd;
    this(bool b) { fromEnd = b; }
    this(size_t i) {
        ops ~= Op("+", i);
    }
    static Index opDollar() {
        return Index(true);
    }
    static Index opIndex(Index idx) {
        return idx;
    }
    static Index opIndex(size_t idx) {
        return Index(idx);
    }
    auto opBinary(string op)(int rhs) {
        Index result = this;
        result.ops ~= Op(op, rhs);
        return result;
    }
    auto value(size_t length) {
        size_t result = fromEnd ? length : 0;
        foreach (e; ops)
            e.apply(result);
        return result;
    }
}

struct Indexer(R) {
    R r;
    alias r this;
    Index opDollar() {
        return Index(true);
    }
    auto opIndex(Index idx) {
        return r[idx.value(r.length)];
    }
    auto opIndex(size_t idx) {
        return r[idx];
    }
}

auto indexer(R)(R r) {
    return Indexer!R(r);
}

unittest {
    auto a = Index[$-2]; // Will always point to next-to-last element.
    auto arr = [1,2,3,4,5,6,7,8,9].indexer; // Wrap access.
    assert(arr[0] == 1); // Regular access still works.
    assert(arr[a] == 8); // Look ma, I'm using $!
    assert(arr[Index[$-1]] == 9); // Look ma, I'm using $!
}

--
  Simen
1 2
Next ›   Last »