Thread overview
Yet another const problem...
May 21, 2012
Mehrdad
May 21, 2012
Chris Cain
May 21, 2012
Mehrdad
May 21, 2012
Chris Cain
May 21, 2012
Mehrdad
May 21, 2012
Chris Cain
May 21, 2012
Chris Cain
May 21, 2012
Mehrdad
May 22, 2012
H. S. Teoh
May 21, 2012
struct Foo
{
	@property bool empty() const { return p == null; }
	@property auto front() inout { return p; }
	void popFront() { p--; }


	// So far so good? okay, let's define save()..

	auto save() inout { return typeof(this)(this.tupleof); }


	// Now let's implement opApply() using the above ^^

	int opApply(void delegate(size_t, typeof(p)) dg) const
	{
		size_t i = 0;
		int r = 0;

		// So far so good...
		for (auto me = this.save(); !r && !me.empty; me.popFront())
		{
			// Whoops, won't work -- 'me' is const! :(
			r = dg(me);
		}

		return r;
	}
}



How do you fix this without duplicating your code?
May 21, 2012
On Monday, 21 May 2012 at 00:07:06 UTC, Mehrdad wrote:
> How do you fix this without duplicating your code?

Simple. Don't use const for input/forward ranges. Input ranges
are more or less only useful if they're mutable because they
mutate basically by definition.

Now the _contents_ of the input range may be immutable or const,
but that's something else all together.
May 21, 2012
On Monday, 21 May 2012 at 00:39:13 UTC, Chris Cain wrote:
> On Monday, 21 May 2012 at 00:07:06 UTC, Mehrdad wrote:
>> How do you fix this without duplicating your code?
>
> Simple. Don't use const for input/forward ranges.

I... guess that works...
May 21, 2012
On Monday, 21 May 2012 at 02:36:26 UTC, Mehrdad wrote:
> On Monday, 21 May 2012 at 00:39:13 UTC, Chris Cain wrote:
>> On Monday, 21 May 2012 at 00:07:06 UTC, Mehrdad wrote:
>>> How do you fix this without duplicating your code?
>>
>> Simple. Don't use const for input/forward ranges.
>
> I... guess that works...

If you want a better answer, you (might) can do it how you want, but it really can't be done without a bit of duplication (inout doesn't seem to work with my idea) and it depends highly on the types you're working with. Some types can do it relatively easily. Some types require some work.

(note: I've never written an opApply and I'm pretty sure yours isn't correct, so I'm doing my best to pseudocode it so it compiles correctly)

For instance, if p is a pointer to something ...
struct Foo(T)
{
    T * p = null;
    @property bool empty() const { return p == null; }
    @property auto front() inout { return p; }
    void popFront() { p--; }

    // Can't use inout here ... bug?
    auto save() const {
        // Transfer const to the contents
        return Foo!(const(T))(this.tupleof);
    }

    auto save() {
        return typeof(this)(this.tupleof);
    }

    int opApply(int delegate(size_t, inout typeof(p)) dg) inout
    {
        size_t i = 0;
        int r = 0;

        for (auto me = this.save(); !r && !me.empty; me.popFront())
        {
            r = dg(i, me.front);
        }

        return r;
    }
}


But overall, doing stuff with a const or immutable input or forward range is just messy because, as I said, it's really not something that can be considered const and actually do something useful.

You might can make an argument that a Random Access Range could be const, and I'd agree. There's some useful things you can do with a const random access range. But an input range? I honestly can't think of a single use other than a really expensive pointer that you "deref" by calling front and check to see if it points to null by calling empty.
May 21, 2012
On Monday, 21 May 2012 at 03:15:52 UTC, Chris Cain wrote:
> You might can make an argument that a Random Access Range could be const, and I'd agree. There's some useful things you can do with a const random access range.


Hmmm... so if you have some (complicated) random-access range, and then you want to overload opApply() (perhaps to let the user use multiple loop variables), how are you supposed to do this without duplicating your code?
May 21, 2012
On Monday, 21 May 2012 at 03:53:08 UTC, Mehrdad wrote:
> Hmmm... so if you have some (complicated) random-access range, and then you want to overload opApply() (perhaps to let the user use multiple loop variables), how are you supposed to do this without duplicating your code?

Your random-access range has opIndex and a length. Just iterate over it using a normal for loop.
May 21, 2012
On Monday, 21 May 2012 at 03:53:08 UTC, Mehrdad wrote:
> Hmmm... so if you have some (complicated) random-access range, and then you want to overload opApply() (perhaps to let the user use multiple loop variables), how are you supposed to do this without duplicating your code?

A random access range has opIndex and a length. Just use a normal for loop in your opApply.

...

for(size_t i = 0; i < this.length; ++i)
    dg(i, this[i]);

...
May 21, 2012
On Monday, 21 May 2012 at 05:09:34 UTC, Chris Cain wrote:
> On Monday, 21 May 2012 at 03:53:08 UTC, Mehrdad wrote:
>> Hmmm... so if you have some (complicated) random-access range, and then you want to overload opApply() (perhaps to let the user use multiple loop variables), how are you supposed to do this without duplicating your code?
>
> A random access range has opIndex and a length. Just use a normal for loop in your opApply.
>
> ...
>
> for(size_t i = 0; i < this.length; ++i)
>     dg(i, this[i]);
>
> ...

d'oh... silly me
May 22, 2012
On Sun, 20 May 2012 20:07:04 -0400, Mehrdad <wfunction@hotmail.com> wrote:

> struct Foo
> {
> 	@property bool empty() const { return p == null; }
> 	@property auto front() inout { return p; }
> 	void popFront() { p--; }
>
>
> 	// So far so good? okay, let's define save()..
>
> 	auto save() inout { return typeof(this)(this.tupleof); }
>
>
> 	// Now let's implement opApply() using the above ^^
>
> 	int opApply(void delegate(size_t, typeof(p)) dg) const
> 	{
> 		size_t i = 0;
> 		int r = 0;
>
> 		// So far so good...
> 		for (auto me = this.save(); !r && !me.empty; me.popFront())
> 		{
> 			// Whoops, won't work -- 'me' is const! :(
> 			r = dg(me);
> 		}
>
> 		return r;
> 	}
> }
>
>
>
> How do you fix this without duplicating your code?

This is yet another cry for tail-const.

It's very obvious we need a clean solution for tail-const before the const story is finished.

The answer for now?  Don't make opApply const.

-Steve
May 22, 2012
On Tue, May 22, 2012 at 02:21:17PM -0400, Steven Schveighoffer wrote:
> On Sun, 20 May 2012 20:07:04 -0400, Mehrdad <wfunction@hotmail.com> wrote:
> 
> >struct Foo
> >{
> >	@property bool empty() const { return p == null; }
> >	@property auto front() inout { return p; }
> >	void popFront() { p--; }
> >
> >
> >	// So far so good? okay, let's define save()..
> >
> >	auto save() inout { return typeof(this)(this.tupleof); }
> >
> >
> >	// Now let's implement opApply() using the above ^^
> >
> >	int opApply(void delegate(size_t, typeof(p)) dg) const
> >	{
> >		size_t i = 0;
> >		int r = 0;
> >
> >		// So far so good...
> >		for (auto me = this.save(); !r && !me.empty; me.popFront())
> >		{
> >			// Whoops, won't work -- 'me' is const! :(
> >			r = dg(me);
> >		}
> >
> >		return r;
> >	}
> >}
> >
> >
> >
> >How do you fix this without duplicating your code?
> 
> This is yet another cry for tail-const.

+1. I badly need it for the new AA implementation too. Otherwise I have to resort to a bunch of incomplete deeply-nested template hacks to create a type with the necessary qualifiers.


> It's very obvious we need a clean solution for tail-const before the const story is finished.
[...]

We also need to sort out the mess that is inout. Currently it works great -- if you only have a single const qualifier to pass through to the return type. It becomes a hairy mess when you have delegate parameters, esp. more than one of them (the delegates have their own "inout scope" which may or may not be tied to the function's inout -- this is inexpressible with the current language).


T

-- 
Without geometry, life would be pointless.