November 16, 2015
On 11/16/2015 12:51 PM, Steven Schveighoffer wrote:
>      List tail() const

I'd like tail to be qualifier-idempotent, i.e. return const for const and non-const for non-const. -- Andrei
November 16, 2015
On 11/16/15 1:37 PM, Andrei Alexandrescu wrote:
> On 11/16/2015 12:51 PM, Steven Schveighoffer wrote:
>>      List tail() const
>
> I'd like tail to be qualifier-idempotent, i.e. return const for const
> and non-const for non-const. -- Andrei

Why? const(int)[] isn't const, why should List!(const(int)) be?

-Steve
November 16, 2015
On 11/16/2015 07:37 PM, Andrei Alexandrescu wrote:
> On 11/16/2015 12:51 PM, Steven Schveighoffer wrote:
>>      List tail() const
>
> I'd like tail to be qualifier-idempotent, i.e. return const for const
> and non-const for non-const. -- Andrei

"Qualifier-idempotent" would rather mean that taking the tail once results in the same qualifier as taking it twice.

https://en.wikipedia.org/wiki/Idempotence
November 16, 2015
On 11/16/2015 02:07 PM, Timon Gehr wrote:
> On 11/16/2015 07:37 PM, Andrei Alexandrescu wrote:
>> On 11/16/2015 12:51 PM, Steven Schveighoffer wrote:
>>>      List tail() const
>>
>> I'd like tail to be qualifier-idempotent, i.e. return const for const
>> and non-const for non-const. -- Andrei
>
> "Qualifier-idempotent" would rather mean that taking the tail once
> results in the same qualifier as taking it twice.
>
> https://en.wikipedia.org/wiki/Idempotence

Jesus. Fine. -- Andrei

November 16, 2015
On 11/16/2015 01:55 PM, Steven Schveighoffer wrote:
> On 11/16/15 1:37 PM, Andrei Alexandrescu wrote:
>> On 11/16/2015 12:51 PM, Steven Schveighoffer wrote:
>>>      List tail() const
>>
>> I'd like tail to be qualifier-idempotent, i.e. return const for const
>> and non-const for non-const. -- Andrei
>
> Why? const(int)[] isn't const, why should List!(const(int)) be?

I'm keeping an eye toward other containers and also more general use. It's a common need. -- Andrei

November 16, 2015
On 11/16/15 2:37 PM, Andrei Alexandrescu wrote:
> On 11/16/2015 01:55 PM, Steven Schveighoffer wrote:
>> On 11/16/15 1:37 PM, Andrei Alexandrescu wrote:
>>> On 11/16/2015 12:51 PM, Steven Schveighoffer wrote:
>>>>      List tail() const
>>>
>>> I'd like tail to be qualifier-idempotent, i.e. return const for const
>>> and non-const for non-const. -- Andrei
>>
>> Why? const(int)[] isn't const, why should List!(const(int)) be?
>
> I'm keeping an eye toward other containers and also more general use.
> It's a common need. -- Andrei

Anywhere const can work, inout should work as well. The one exception as I've described is when you create and build a (im)mutable result, then converting it to const. In this case, you have to build it at once. Otherwise, the compiler can't tell whether this mutable thing you are constructing is safe to cast to inout (and whatever flavor it needs to cast to upon exit).

In this case, it appears to work only because of your cast of _allocator to mutable whenever you access it via allocator. Other than that, the only other member is the node pointer, which is const. Effectively, A list's data is always const, so there is no reason to make the struct itself const.

-Steve
November 16, 2015
On 11/16/2015 03:38 PM, Steven Schveighoffer wrote:
> On 11/16/15 2:37 PM, Andrei Alexandrescu wrote:
>> On 11/16/2015 01:55 PM, Steven Schveighoffer wrote:
>>> On 11/16/15 1:37 PM, Andrei Alexandrescu wrote:
>>>> On 11/16/2015 12:51 PM, Steven Schveighoffer wrote:
>>>>>      List tail() const
>>>>
>>>> I'd like tail to be qualifier-idempotent, i.e. return const for const
>>>> and non-const for non-const. -- Andrei
>>>
>>> Why? const(int)[] isn't const, why should List!(const(int)) be?
>>
>> I'm keeping an eye toward other containers and also more general use.
>> It's a common need. -- Andrei
>
> Anywhere const can work, inout should work as well. The one exception as
> I've described is when you create and build a (im)mutable result, then
> converting it to const. In this case, you have to build it at once.
> Otherwise, the compiler can't tell whether this mutable thing you are
> constructing is safe to cast to inout (and whatever flavor it needs to
> cast to upon exit).

I think the main problem is the difficulty of getting from "I want to make this method work with mutable and nonconst data" to "I have a working solution using inout".

The semantics is complex and difficult. Error messages are horrifying. Even explaining code that works is hard. Once a solution works, trying to change it in meaningful ways again makes the code not work, and again with uninformative error messages. So there's resistance to changing working code.

At the same time, we have a wonderful language ready to help with features that didn't exist when we introduced inout. Search http://dpaste.dzfl.pl/52a3013efe34 for QList - it's restricted properly, works very well, and is easy to explain.

Walter and I think inout didn't turn out well. It became a little monster mastering a swamp. Template solutions can drain that swamp, make the monster disappear, and build nice things on that field.

I think we should slowly marginalize inout - discourage it from new code, document and publicize better alternatives, the works.

> In this case, it appears to work only because of your cast of _allocator
> to mutable whenever you access it via allocator. Other than that, the
> only other member is the node pointer, which is const. Effectively, A
> list's data is always const, so there is no reason to make the struct
> itself const.

Plenty of reason. The list may be member in an object. Also, if you don't have const List you can't compose List with itself.


Andrei


November 16, 2015
On Monday, 16 November 2015 at 18:37:18 UTC, Andrei Alexandrescu wrote:
> On 11/16/2015 12:51 PM, Steven Schveighoffer wrote:
>>      List tail() const
>
> I'd like tail to be qualifier-idempotent, i.e. return const for const and non-const for non-const. -- Andrei

I don't have a compiler at hand right now, but doesn't this work?

auto tail(this U)() const
{
  assert(root);
  auto n = root.next;
  incRef(n);
  return U(n, allocator);
}

November 16, 2015
On Monday, 16 November 2015 at 20:48:49 UTC, Andrei Alexandrescu wrote:
> I think we should slowly marginalize inout - discourage it from new code, document and publicize better alternatives, the works.

When you can templatize the function on the type that would otherwise be inout, then there really isn't any reason to use inout. But what do you do when you're dealing with a member function - especially on a class? I don't know of any way to templatize a member function on the type of the this pointer/reference, but maybe there's a template feature with which I'm not familiar enough. But even if there _is_ such a feature, it won't work for virtual functions.

So, while I completely agree that it's cleaner to avoid inout when it's not needed, from what I can see, there are cases where you really don't have an alternative aside from duplicating the entire function. So, it does seem like on some level, we're stuck using inout, even if we can avoid it in many cases.

- Jonathan M Davis
November 16, 2015
On 11/16/2015 10:18 PM, Jonathan M Davis wrote:
> I don't know of any way to templatize a member function on the type of
> the this pointer/reference, but maybe there's a template feature with
> which I'm not familiar enough.

Indeed there is.

class C{
    void foo(this T)(){ pragma(msg, T); }
}

class D: C{}

void main(){
    auto c=new const(C)();
    c.foo();
    auto d=new immutable(D)();
    d.foo();
}

> But even if there _is_ such a feature, it won't work for virtual functions.

Yup.