August 15, 2020
On Fri, Aug 14, 2020 at 11:50 PM Andrei Alexandrescu via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 8/4/20 12:57 PM, Timon Gehr wrote:
> >> * Every time "inout" comes within a radius of a mile of what I'm doing, it starts to stink like a skunk. I wish I could get a restraining order. I can't instantiate "inout" variables, so writing any tests or constraints becomes an advanced matter of defining functions and crap.
> >
> > I have never understood the `(inout int){ T.init; }` idiom. Just use `(T
> > value){ value; }`.
> >
> >> I get frustrated, I protest to this forum, and immediately a cabal is
> >> raised under Timon's leadership. The cabal convinces me that inout is
> >> actually great and that I'm an idiot. I do get convinced, which is
> >> more of a proof that Timon is very good, than a testament to the
> >> conviviality of inout. Then I leave and get back to my code, and it
> >> stinks of inout again. And I hate it and myself for having to deal
> >> with it.
> >> ...
> >
> > I am sure you are sincere, but I still think this is a misrepresentation. I don't think I ever claimed that `inout` is great. I merely understand what `inout` is supposed to be, but it comes way short. See all of the issues I have opened that show that type checking for `inout` is broken. When I tried to document inout properly in 2018 I found multiple new type system holes, I think they are open to this day.
>
> Well for what it's worth I have a simple question: how can I assess in druntime if a type T is copyable? I add the informal requirement that it's a simple query so it should be served with a proportionally simple answer.
>
> My initial take:
>
> static if (is(typeof((T x) { T y = x; }))) { ... }
>
> i.e. a lambda can be created that takes a T and creates a copy of it. Beautiful.
>
> This test, however, passes for inout types. And inout types cannot be considered really copyable, because they cannot be used in many places where one would expect to use a copyable type. To wit, a variety of unittests will fail (such as structs with copyable members), all protesting to the attempt of classifying inout types as copyable.
>
> Second attempt:
>
> static if (is(typeof((T x) { T y = x; })) && !is(T == inout U, U) { ... }
>
> So a type is copyable as before, just let's special case inout for exclusion.
>
> This already gets my diaper in a bunch because I need to special case a type of which utility I already am suspicious. And it's not only here - it's many, many similar places.
>
> Also, this also does NOT work because inout(const(int)) passes the test.
> This could probably be classified as a bug in the language or its compiler.
>
> So now I'm looking at things like importing "core.lifetime : emplace" and see if that compiles. Because the very complex implementation of emplace uses a complex mechanism to handle inout.
>
> I could be convinced that this awful complexity is justified given the choices made in the definition of this or that, but it would be more difficult to convince ourselves this is good programming language design. Simple questions should have simple answers.
>

I'm delighted by this post. Keep talking this way, and we have a chance to make the kind of progress we've been waiting for!

If you're looking into emplace (and friends) you should be familiar with Suleyman's move constructor work-in-progress. I am convinced that's the only reasonable path forwards on that front.


August 15, 2020
On 14.08.20 15:45, Andrei Alexandrescu wrote:
> On 8/4/20 12:57 PM, Timon Gehr wrote:
>>> * Every time "inout" comes within a radius of a mile of what I'm doing, it starts to stink like a skunk. I wish I could get a restraining order. I can't instantiate "inout" variables, so writing any tests or constraints becomes an advanced matter of defining functions and crap.
>>
>> I have never understood the `(inout int){ T.init; }` idiom. Just use `(T value){ value; }`.
>>
>>> I get frustrated, I protest to this forum, and immediately a cabal is raised under Timon's leadership. The cabal convinces me that inout is actually great and that I'm an idiot. I do get convinced, which is more of a proof that Timon is very good, than a testament to the conviviality of inout. Then I leave and get back to my code, and it stinks of inout again. And I hate it and myself for having to deal with it.
>>> ...
>>
>> I am sure you are sincere, but I still think this is a misrepresentation. I don't think I ever claimed that `inout` is great. I merely understand what `inout` is supposed to be, but it comes way short. See all of the issues I have opened that show that type checking for `inout` is broken. When I tried to document inout properly in 2018 I found multiple new type system holes, I think they are open to this day.
> 
> Well for what it's worth I have a simple question: how can I assess in druntime if a type T is copyable? I add the informal requirement that it's a simple query so it should be served with a proportionally simple answer.
> 
> My initial take:
> 
> static if (is(typeof((T x) { T y = x; }))) { ... }
> 
> i.e. a lambda can be created that takes a T and creates a copy of it. Beautiful.
> 
> This test, however, passes for inout types. And inout types cannot be considered really copyable, because they cannot be used in many places where one would expect to use a copyable type. To wit, a variety of unittests will fail (such as structs with copyable members), all protesting to the attempt of classifying inout types as copyable.
> ...

Those are protesting to the attempt of using `inout` types as member variables, not to the attempt of classifying them as copyable. The question is whether we really want types that can't be used as fields, and I think no, we don't want that. (But all of those weird limitations would not exist if `inout` was designed right!)

> ...
> Also, this also does NOT work because inout(const(int)) passes the test. This could probably be classified as a bug in the language or its compiler.
> ...

It most definitely is.

> So now I'm looking at things like importing "core.lifetime : emplace" and see if that compiles. Because the very complex implementation of emplace uses a complex mechanism to handle inout.
> 
> I could be convinced that this awful complexity is justified given the choices made in the definition of this or that, but it would be more difficult to convince ourselves this is good programming language design.

The complexity is not justified, it is not good programming language design and I never claimed otherwise. Good programming language features are orthogonal to each other.

> Simple questions should have simple answers.

If the above implementation of isCopyable is not the one you want, something else is off.
August 15, 2020
On 15.08.20 03:29, Timon Gehr wrote:
> 
>> Simple questions should have simple answers.
> 
> If the above implementation of isCopyable is not the one you want, something else is off.

Oh, actually I misspoke, didn't notice that you changed my suggestion from the github issue from __traits(compiles, ...) to is(typeof(...)).

Use __traits(compiles, ...), not is(typeof(...)). typeof assigns types to expressions that would not otherwise compile, even lambdas:

void main(){
    int x;
    static void foo(){
        pragma(msg, typeof(()=>x));             // int delegate() pure nothrow @nogc @safe
        pragma(msg, __traits(compiles, ()=>x)); // false
    }
}

There is this persistent myth that __traits(compiles, ...) is the same as is(typeof(...)) this is not the case and IIRC I have used it to demonstrate that most template constraints in Phobos don't work correctly.
August 17, 2020
On 8/14/20 4:30 PM, IGotD- wrote:
> On Sunday, 2 August 2020 at 20:50:14 UTC, Andrei Alexandrescu wrote:
>>
>> A "Define All Qualifiers" DIP would be a radical improvement of the state of affairs.
> 
> Make sure there is a separate DIP for each qualifier. I have a feeling some qualifiers will be quite a battle with different opinions.

Conversions and interactions make this a package deal.
August 17, 2020
On 8/14/20 9:35 PM, Timon Gehr wrote:
> On 15.08.20 03:29, Timon Gehr wrote:
>>
>>> Simple questions should have simple answers.
>>
>> If the above implementation of isCopyable is not the one you want, something else is off.
> 
> Oh, actually I misspoke, didn't notice that you changed my suggestion from the github issue from __traits(compiles, ...) to is(typeof(...)).
> 
> Use __traits(compiles, ...), not is(typeof(...)). typeof assigns types to expressions that would not otherwise compile, even lambdas:
> 
> void main(){
>      int x;
>      static void foo(){
>          pragma(msg, typeof(()=>x));             // int delegate() pure nothrow @nogc @safe
>          pragma(msg, __traits(compiles, ()=>x)); // false
>      }
> }
> 
> There is this persistent myth that __traits(compiles, ...) is the same as is(typeof(...)) this is not the case and IIRC I have used it to demonstrate that most template constraints in Phobos don't work correctly.

This is a problem as __traits should be a lower-level, seldom-used resort. Are the distinctions between the two documented somewhere?
August 24, 2020
On Sunday, 2 August 2020 at 20:50:14 UTC, Andrei Alexandrescu wrote:
> (Background: qualifiers were introduced following my horror when I started writing D1 code and saw that strings are represented as char[]. So structs with string members would look like:
>
> [...]

Nearly every time I use inout I run into problems. Something doesn't compile, I get annoyed, and switch to templated this. Can anyone tell me what inout does that templated this doesn't?
August 24, 2020
On 8/24/20 12:13 PM, Atila Neves wrote:
> On Sunday, 2 August 2020 at 20:50:14 UTC, Andrei Alexandrescu wrote:
>> (Background: qualifiers were introduced following my horror when I started writing D1 code and saw that strings are represented as char[]. So structs with string members would look like:
>>
>> [...]
> 
> Nearly every time I use inout I run into problems. Something doesn't compile, I get annoyed, and switch to templated this.

I have the opposite experience -- inout just works most of the time.

> Can anyone tell me what inout does that templated this doesn't?

1. It does not create a separate identical implementation for all flavors of mutability
2. It is not mutable even when the this reference is.

The problems with inout have nothing to do with its utility. They are artificial limitations that should not have been included.

-Steve
August 24, 2020
On 24.08.20 18:27, Steven Schveighoffer wrote:
> 
>> Can anyone tell me what inout does that templated this doesn't?
> 
> 1. It does not create a separate identical implementation for all flavors of mutability

- It works with virtual functions/function pointers/delegates.
- The implementation is type checked before instantiation.

> 2. It is not mutable even when the this reference is.

(That's a hard property to keep in a sound version without artificial limitations.)
August 24, 2020
On 8/24/20 1:09 PM, Timon Gehr wrote:
> On 24.08.20 18:27, Steven Schveighoffer wrote:

>> 2. It is not mutable even when the this reference is.
> 
> (That's a hard property to keep in a sound version without artificial limitations.)

I'm not sure what you mean -- inout is like const in that you cannot mutate data via that reference.

with template this parameters, you can statically check if it's mutable, and mutate.

It is only important if your goal is to identify that a function will not modify data passed to it. In essence, if you care about const functions.

-Steve
August 24, 2020
On 24.08.20 19:19, Steven Schveighoffer wrote:
>>
> 
> I'm not sure what you mean -- inout is like const in that you cannot mutate data via that reference.

With a better type system the caller might pass in delegates that know about the mutability.

void foo[Qualifier q](ref q int x, void delegate(ref q int x) process){
    process(x);
}

void main(){
    int x=0;
    foo(x,(ref int a){ a=2; }); // (q is mutable)
    immutable int y=2;
    foo(y,(ref immutable int b){ x=b; }); // (q is `immutable`)
}