www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - So what does (inout int = 0) do?

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Consider:

https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152

There is no explanation to it in the source code, and the line blames to 
https://github.com/D-Programming-Language/phobos/pull/2661 (irrelevant).

Commenting it out yields a number of unittest compilation errors, 
neither informative about the root of the problem and indicative as to 
how the parameter solves it.

There are two issues here:

1. Once a problem/solution pair of this degree of subtlety crops up, we 
need to convince ourselves that that's sensible. If we deem it not so, 
we look into improving the language to make the problem disappear.

2. There needs to be documentation for people working on the standard 
library so they don't need to waste time on their own discovery process.

We want Phobos to be beautiful, a prime example of good D code. 
Admittedly, it also needs to be very general and efficient, which 
sometimes gets in the way. But we cannot afford an accumulation of mad 
tricks to obscure the large design.


Andrei
Apr 14 2016
next sibling parent Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Friday, 15 April 2016 at 03:10:12 UTC, Andrei Alexandrescu 
wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152

 There is no explanation to it in the source code, and the line 
 blames to 
 https://github.com/D-Programming-Language/phobos/pull/2661 
 (irrelevant).
I heard that sometimes git blame can track moving data between files, but this is not such a case. You can continue the blame by blaming the old path starting from the commit preceding that one: git blame c828a08b64de067eeb2ddcad8fed22b2cd92e438^ -- range.d
 Commenting it out yields a number of unittest compilation 
 errors, neither informative about the root of the problem and 
 indicative as to how the parameter solves it.
I'm not sure what errors you saw but the first one is that this fails: static assert( isInputRange!(inout(int)[])); git blame on this line points to: https://issues.dlang.org/show_bug.cgi?id=7824
Apr 14 2016
prev sibling next sibling parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thursday, April 14, 2016 23:10:12 Andrei Alexandrescu via Digitalmars-d 
wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primi
 tives.d#L152

 There is no explanation to it in the source code, and the line blames to
 https://github.com/D-Programming-Language/phobos/pull/2661 (irrelevant).

 Commenting it out yields a number of unittest compilation errors,
 neither informative about the root of the problem and indicative as to
 how the parameter solves it.

 There are two issues here:

 1. Once a problem/solution pair of this degree of subtlety crops up, we
 need to convince ourselves that that's sensible. If we deem it not so,
 we look into improving the language to make the problem disappear.

 2. There needs to be documentation for people working on the standard
 library so they don't need to waste time on their own discovery process.

 We want Phobos to be beautiful, a prime example of good D code.
 Admittedly, it also needs to be very general and efficient, which
 sometimes gets in the way. But we cannot afford an accumulation of mad
 tricks to obscure the large design.
IIRC, the problem has to do with ranges of inout elements working correctly, which gets really funky, because inout is a temporary thing and not a full-on type constructor/qualifier. I believe that Kenji is the one that implemented the fix, and I think that he explained it in the newsgroup at some point. Certainly, there have been a few times that it's come up in D.Learn when folks ask what the heck it is, so there should be a few posts floating around with an explanation. This is the only useful post that I could find in a quick search though: http://forum.dlang.org/post/mh68p8$2p56$1 digitalmars.com inout attempts to solve a very real problem, but it does seem to be surprisingly hard to understand and use prooperly even though it's theoretically simple. And this bit with ranges is a quirk that I think very few people understand and remember. I usually forget exactly what it does when it comes up and have to try and dig through the newsgroup archives for a previous discussion on it. - Jonathan M Davis
Apr 14 2016
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 12:23 AM, Jonathan M Davis via Digitalmars-d wrote:
 On Thursday, April 14, 2016 23:10:12 Andrei Alexandrescu via Digitalmars-d
 wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primi
 tives.d#L152

 There is no explanation to it in the source code, and the line blames to
 https://github.com/D-Programming-Language/phobos/pull/2661 (irrelevant).

 Commenting it out yields a number of unittest compilation errors,
 neither informative about the root of the problem and indicative as to
 how the parameter solves it.

 There are two issues here:

 1. Once a problem/solution pair of this degree of subtlety crops up, we
 need to convince ourselves that that's sensible. If we deem it not so,
 we look into improving the language to make the problem disappear.

 2. There needs to be documentation for people working on the standard
 library so they don't need to waste time on their own discovery process.

 We want Phobos to be beautiful, a prime example of good D code.
 Admittedly, it also needs to be very general and efficient, which
 sometimes gets in the way. But we cannot afford an accumulation of mad
 tricks to obscure the large design.
IIRC, the problem has to do with ranges of inout elements working correctly, which gets really funky, because inout is a temporary thing and not a full-on type constructor/qualifier. I believe that Kenji is the one that implemented the fix, and I think that he explained it in the newsgroup at some point. Certainly, there have been a few times that it's come up in D.Learn when folks ask what the heck it is, so there should be a few posts floating around with an explanation. This is the only useful post that I could find in a quick search though: http://forum.dlang.org/post/mh68p8$2p56$1 digitalmars.com inout attempts to solve a very real problem, but it does seem to be surprisingly hard to understand and use prooperly even though it's theoretically simple. And this bit with ranges is a quirk that I think very few people understand and remember. I usually forget exactly what it does when it comes up and have to try and dig through the newsgroup archives for a previous discussion on it. - Jonathan M Davis
I think we should deprecate inout. For real. It costs way too much for what it does. For all I can tell most of D's proponents don't know how it works. -- Andrei
Apr 14 2016
next sibling parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Friday, 15 April 2016 at 05:38:56 UTC, Andrei Alexandrescu 
wrote:
 I think we should deprecate inout. For real. It costs way too 
 much for what it does. For all I can tell most of D's 
 proponents don't know how it works. -- Andrei
What would replace it in the case of classes, where you can't have templated virtual methods? Perhaps a mechanism to declare a templated virtual function, and a list of instantiations which will go into the vtable? If we are to kill inout and replace it with something else, then it should support cases where inout failed, such as inout on the parameter of a delegate passed to the function (see: inout opApply).
Apr 14 2016
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 02:15 AM, Vladimir Panteleev wrote:
 On Friday, 15 April 2016 at 05:38:56 UTC, Andrei Alexandrescu wrote:
 I think we should deprecate inout. For real. It costs way too much for
 what it does. For all I can tell most of D's proponents don't know how
 it works. -- Andrei
What would replace it in the case of classes, where you can't have templated virtual methods? Perhaps a mechanism to declare a templated virtual function, and a list of instantiations which will go into the vtable?
You write several one-liners that forward to an internal template implementation.
 If we are to kill inout and replace it with something else, then it
 should support cases where inout failed, such as inout on the parameter
 of a delegate passed to the function (see: inout opApply).
I think we should kill it, period. No replacement is really needed, for the most part most problems with inout are complications caused by the very presence of inout. C++ has no parameterization of qualifiers and lived with it like a guy lives with a bald spot on a butt cheek. There's not even a proposal I can remember to fix that. Those duplications are the least of C++'s, or any langauge's, problems. inout must go. Andrei
Apr 15 2016
prev sibling next sibling parent Guillaume Piolat <name.lastname gmail.com> writes:
On Friday, 15 April 2016 at 05:38:56 UTC, Andrei Alexandrescu 
wrote:
 I think we should deprecate inout. For real. It costs way too 
 much for what it does. For all I can tell most of D's 
 proponents don't know how it works. -- Andrei
This is the explanation I came up with, not sure if accurate. And only to teach myself the basics. https://p0nce.github.io/d-idioms/#Knowing-inout-inside-out I'm all for removing things!
Apr 15 2016
prev sibling next sibling parent reply Kenji Hara via Digitalmars-d <digitalmars-d puremagic.com> writes:
2016-04-15 14:38 GMT+09:00 Andrei Alexandrescu via Digitalmars-d <
digitalmars-d puremagic.com>:

 On 04/15/2016 12:23 AM, Jonathan M Davis via Digitalmars-d wrote:

 On Thursday, April 14, 2016 23:10:12 Andrei Alexandrescu via Digitalmars-d
 wrote:

 Consider:


 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primi
 tives.d#L152

 There is no explanation to it in the source code, and the line blames to
 https://github.com/D-Programming-Language/phobos/pull/2661 (irrelevant).

 Commenting it out yields a number of unittest compilation errors,
 neither informative about the root of the problem and indicative as to
 how the parameter solves it.

 There are two issues here:

 1. Once a problem/solution pair of this degree of subtlety crops up, we
 need to convince ourselves that that's sensible. If we deem it not so,
 we look into improving the language to make the problem disappear.

 2. There needs to be documentation for people working on the standard
 library so they don't need to waste time on their own discovery process.

 We want Phobos to be beautiful, a prime example of good D code.
 Admittedly, it also needs to be very general and efficient, which
 sometimes gets in the way. But we cannot afford an accumulation of mad
 tricks to obscure the large design.
IIRC, the problem has to do with ranges of inout elements working correctly, which gets really funky, because inout is a temporary thing and not a full-on type constructor/qualifier. I believe that Kenji is the one that implemented the fix, and I think that he explained it in the newsgroup at some point. Certainly, there have been a few times that it's come up in D.Learn when folks ask what the heck it is, so there should be a few posts floating around with an explanation. This is the only useful post that I could find in a quick search though: http://forum.dlang.org/post/mh68p8$2p56$1 digitalmars.com inout attempts to solve a very real problem, but it does seem to be surprisingly hard to understand and use prooperly even though it's theoretically simple. And this bit with ranges is a quirk that I think very few people understand and remember. I usually forget exactly what it does when it comes up and have to try and dig through the newsgroup archives for a previous discussion on it. - Jonathan M Davis
I think we should deprecate inout. For real. It costs way too much for what it does. For all I can tell most of D's proponents don't know how it works. -- Andrei
You should recall the history of inout. http://wiki.dlang.org/DIP2 At first, It has designed to temporarily squash mutable/const/immutable qualifiers on function argument inside function body. Therefore when inout qualifier appears in function return type, but doesn't on parameter types, we defined it an error. However, I concluded that we can remove the restriction. When an inout object arises from thin air, no one holds the qualifier of its real instance. It means, the returned inout object can be converted to arbitrary qualifiers without type system breaking. Two years ago I opened an enhancement issue: https://issues.dlang.org/show_bug.cgi?id=13006 And posted a pull request. https://github.com/D-Programming-Language/dmd/pull/3740 Kenji Hara
Apr 15 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 06:50 AM, Kenji Hara via Digitalmars-d wrote:
 You should recall the history of inout.
 http://wiki.dlang.org/DIP2

 At first, It has designed to temporarily squash mutable/const/immutable
 qualifiers on function argument inside function body. Therefore when
 inout qualifier appears in function return type, but doesn't on
 parameter types, we defined it an error.

 However, I concluded that we can remove the restriction. When an inout
 object arises from thin air, no one holds the qualifier of its real
 instance. It means, the returned inout object can be converted to
 arbitrary qualifiers without type system breaking.

 Two years ago I opened an enhancement issue:
 https://issues.dlang.org/show_bug.cgi?id=13006

 And posted a pull request.
 https://github.com/D-Programming-Language/dmd/pull/3740
I do recall the history. The enhancement is sensible in a frame of mind "we need to keep inout" but I'm looking at top level and thinking, crap we can put up with a little duplication if we get rid of inout. It's just a prime example of apparently simple idea gone bonkers. In all of my recent code I've avoided inout and didn't miss it one bit. There's no need for it - I replace it with one-liners that forward to template functions, which in turn deduce qualifiers and all is great. Andrei
Apr 15 2016
parent reply Kenji Hara via Digitalmars-d <digitalmars-d puremagic.com> writes:
2016-04-15 22:41 GMT+09:00 Andrei Alexandrescu via Digitalmars-d <
digitalmars-d puremagic.com>:

 On 04/15/2016 06:50 AM, Kenji Hara via Digitalmars-d wrote:

 You should recall the history of inout.
 http://wiki.dlang.org/DIP2

 At first, It has designed to temporarily squash mutable/const/immutable
 qualifiers on function argument inside function body. Therefore when
 inout qualifier appears in function return type, but doesn't on
 parameter types, we defined it an error.

 However, I concluded that we can remove the restriction. When an inout
 object arises from thin air, no one holds the qualifier of its real
 instance. It means, the returned inout object can be converted to
 arbitrary qualifiers without type system breaking.

 Two years ago I opened an enhancement issue:
 https://issues.dlang.org/show_bug.cgi?id=13006

 And posted a pull request.
 https://github.com/D-Programming-Language/dmd/pull/3740
I do recall the history. The enhancement is sensible in a frame of mind "we need to keep inout" but I'm looking at top level and thinking, crap we can put up with a little duplication if we get rid of inout. It's just a prime example of apparently simple idea gone bonkers. In all of my recent code I've avoided inout and didn't miss it one bit. There's no need for it - I replace it with one-liners that forward to template functions, which in turn deduce qualifiers and all is great.
Didn't you use array.dup until now? It's a good example to handle qualifiers with inout. It's not sensible at all, inout is already well-defined and has much power in D's type system. Removing it is just a foolish idea. If you worry the future of Phobos written in D, first you would need to think about safe. It's yet not well defined/implemented by compiler, and many Phobos code are marked as trusted. Does it has lower priority than complain to a small hack for the *current* inout limitation? I just cannot agree your argument. Kenji Hara
Apr 15 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 12:19 PM, Kenji Hara via Digitalmars-d wrote:
 Didn't you use array.dup until now? It's a good example to handle
 qualifiers with inout.
Would it be difficult to make it work without inout?
 It's not sensible at all, inout is already well-defined and has much
 power in D's type system.
 Removing it is just a foolish idea.
What are a few examples of the power of inout? What things does inout afford us, that would be otherwise not achievable?
 If you worry the future of Phobos written in D, first you would need to
 think about  safe.
 It's yet not well defined/implemented by compiler, and many Phobos code
 are marked as  trusted.
 Does it has lower priority than complain to a small hack for the
 *current* inout limitation?
The thing about safe is it does enable things that otherwise would not be possible. Overall I agree there are plenty of things that deserve a revisit, but just putting in competition things against one another is unlikely to shed light on their technical merit.
 I just cannot agree your argument.
I understand and would like to be better informed. So could you please substantiate your argument by replying to the questions above? To restate my arguments: 1. The motivation of inout is clear and simple - have it as a placeholder for any other qualifier so as to avoid the method duplication observed in C++. However, it has over time grown into a feature of which complexity way transcends its small usefulness. 2. Attempting to make inout useful have created their own problems, solving which in turn have increased its complexity. This cycle of accretions has led over time to a vortex of oddity in the middle of the type system. 3. For all problems that inout is purported to solve, I know of idioms that are definitely simpler and overall almost as good if not better. So a hard question is whether the existence is justified. There are 306 uses of inout in Phobos. A good thing to do would be an investigation of their usefulness. Andrei
Apr 15 2016
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 15 April 2016 at 17:11:39 UTC, Andrei Alexandrescu 
wrote:
 3. For all problems that inout is purported to solve, I know of 
 idioms that are definitely simpler and overall almost as good 
 if not better. So a hard question is whether the existence is 
 justified.
If it's something to be avoided except in particular cases, then I suggest that is made clear in the documentation. I had not realized that the main reason that inout was added was because of not being able to use templates as virtual functions in classes.
Apr 15 2016
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 02:24 PM, jmh530 wrote:
 I had not realized that the main reason that inout was added was because
 of not being able to use templates as virtual functions in classes.
The main reason is actually avoiding code duplication is such situations (i.e. the problem has a solution just not ideal). -- Andrei
Apr 15 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/15/16 1:11 PM, Andrei Alexandrescu wrote:
 On 04/15/2016 12:19 PM, Kenji Hara via Digitalmars-d wrote:
 Didn't you use array.dup until now? It's a good example to handle
 qualifiers with inout.
Would it be difficult to make it work without inout?
T[] dup(T)(T[] arr) this should be doable. Problem is, you get identical instantiations for each of the different qualifiers, unless you take different actions based on the mutability. Instead: inout(T)[] dup(T)(inout(T)[] arr) This is one instantiation, no matter the mutability of the parameter. And it guarantees no molestation of arr, even if it's mutable. This would be much more difficult with a struct/class, whereas with inout it's pretty simple. But I think dup as it is defined is a bad example, because as defined by the language, it implies that you want a mutable version where the source may be const/immutable. In the general sense, a duplication function where you get the *same* mutability is more applicable for inout.
 It's not sensible at all, inout is already well-defined and has much
 power in D's type system.
 Removing it is just a foolish idea.
What are a few examples of the power of inout?
One thing to understand is that inout is only valuable if you think const is valuable. That is, you think the advertisement of "no, I will not modify this parameter, give me whatever you got" is of value. If you don't care, then templates suffice. void popFront(T)(inout(T)[] arr) This is possible only with inout. const will not cut it. The current definition is: void popFront(T)(T[] arr) which is OK, but there is no advertisement that popFront doesn't change any data in arr.
 What things does inout afford us, that would be otherwise not achievable?
advertisement of const without having to cast the type to const (and all the limitations that entail).
 If you worry the future of Phobos written in D, first you would need to
 think about  safe.
 It's yet not well defined/implemented by compiler, and many Phobos code
 are marked as  trusted.
 Does it has lower priority than complain to a small hack for the
 *current* inout limitation?
The thing about safe is it does enable things that otherwise would not be possible. Overall I agree there are plenty of things that deserve a revisit, but just putting in competition things against one another is unlikely to shed light on their technical merit.
I think the point of Kenji's argument is that inout's current limitations are what you are bumping into, and those limitations are unnecessary and arbitrary. We can make inout better and more consistent. Pretty easily actually. We can certainly fix the inout int = 0 problem.
 To restate my arguments:

 1. The motivation of inout is clear and simple - have it as a
 placeholder for any other qualifier so as to avoid the method
 duplication observed in C++. However, it has over time grown into a
 feature of which complexity way transcends its small usefulness.
It has some rough edges. These can be smoothed out.
 2. Attempting to make inout useful have created their own problems,
 solving which in turn have increased its complexity. This cycle of
 accretions has led over time to a vortex of oddity in the middle of the
 type system.
This is not what you are railing against. The current limitations (and their workarounds) are self-imposed, we just need to get rid of them. An interesting thing is that when you involve generic code, certain "good ideas" become very inconvenient. For example, this whole discussion is based on the idea that "if you don't have any inout parameters, why are you declaring inout variables? It makes no sense!" A very sensible and logical idea. But when you want code that works with or without inout variables, all of a sudden the same code is throwing an error in some cases because of an unforeseen situation. Another example is code like this: int firstInt(T...)(T t) { foreach(ref x; t) { static if(typeof(x) == int) return x; } return -1; } This throws an error because if there is an int in T..., then return -1 statement is "unreachable". This is similar to the inout int = 0 case. In non-generic code, it makes sense to flag code that won't be reached. But in generic code, it's actually not a true statement for all instantiations of the template! It's another limitation we should relax.
 3. For all problems that inout is purported to solve, I know of idioms
 that are definitely simpler and overall almost as good if not better. So
 a hard question is whether the existence is justified.
I know of no "better" idioms, in terms of code generation and specificity, to replace inout (or at least, inout as I envision it should be). Care to elaborate? -Steve
Apr 15 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 4/15/16 2:39 PM, Steven Schveighoffer wrote:
 On 4/15/16 1:11 PM, Andrei Alexandrescu wrote:
 On 04/15/2016 12:19 PM, Kenji Hara via Digitalmars-d wrote:
 Didn't you use array.dup until now? It's a good example to handle
 qualifiers with inout.
Would it be difficult to make it work without inout?
T[] dup(T)(T[] arr) this should be doable. Problem is, you get identical instantiations for each of the different qualifiers, unless you take different actions based on the mutability. Instead: inout(T)[] dup(T)(inout(T)[] arr) This is one instantiation, no matter the mutability of the parameter. And it guarantees no molestation of arr, even if it's mutable. This would be much more difficult with a struct/class, whereas with inout it's pretty simple. But I think dup as it is defined is a bad example, because as defined by the language, it implies that you want a mutable version where the source may be const/immutable. In the general sense, a duplication function where you get the *same* mutability is more applicable for inout.
A better support for this argument is std.array.replaceSlice at https://github.com/D-Programming-Language/phobos/blob/master/std/array.d#L2594: inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement); So here we are guaranteed that (a) the result type is the same as the first argument, and (b) the first argument is never modified even if a mutable slice is passed. I agree this is a nice property, and one that C++ does not enjoy. The questions are of course how important it is, how useful it is, and what price it is worth. Getting back to replaceSlice I'd define it in more flexible terms anyway. So I'd eliminate the use of inout there even if it did add value.
 It's not sensible at all, inout is already well-defined and has much
 power in D's type system.
 Removing it is just a foolish idea.
What are a few examples of the power of inout?
One thing to understand is that inout is only valuable if you think const is valuable. That is, you think the advertisement of "no, I will not modify this parameter, give me whatever you got" is of value. If you don't care, then templates suffice.
This is correct but incomplete. The omitted part is that inout is valuable when $EVERYTHING_YOU_SAID and also you want to transport the qualifier from an argument to the result. This diminishes its importance considerably for all folks, including those for whom const is important.
 void popFront(T)(inout(T)[] arr)
void popFront(T)(ref inout(T)[] arr); (point stays)
 This is possible only with inout. const will not cut it. The current
 definition is:

 void popFront(T)(T[] arr)

 which is OK, but there is no advertisement that popFront doesn't change
 any data in arr.
Should this work as well? void popFront(ref const(T)[] arr); In other words conversion of ref T[] to ref const(T)[] is sound.
 What things does inout afford us, that would be otherwise not achievable?
advertisement of const without having to cast the type to const (and all the limitations that entail).
 If you worry the future of Phobos written in D, first you would need to
 think about  safe.
 It's yet not well defined/implemented by compiler, and many Phobos code
 are marked as  trusted.
 Does it has lower priority than complain to a small hack for the
 *current* inout limitation?
The thing about safe is it does enable things that otherwise would not be possible. Overall I agree there are plenty of things that deserve a revisit, but just putting in competition things against one another is unlikely to shed light on their technical merit.
I think the point of Kenji's argument is that inout's current limitations are what you are bumping into, and those limitations are unnecessary and arbitrary. We can make inout better and more consistent. Pretty easily actually. We can certainly fix the inout int = 0 problem.
I'm not sure - at all in fact. This is at the tail of a sequence of changes of inout, and there is a history of such mini-designs leading to complications, regressions, and further mini-designs. It's the wolf that eats the dog that eats the cat that eats the mouse. We fix this and there's another and then another and then another. It has already happened. In fact Walter's and my design of inout is a prime example of the failings of that approach. We underdesigned it to work in a few small examples. We knew we lost when Kenji figured inout must be a type qualifier (we were hoping to getting away with it being a sort of a placeholder/wildcard). I think we should have pulled it back then. It's become a mini-monster that only wants more attention, more language changes, more work.
 An interesting thing is that when you involve generic code, certain
 "good ideas" become very inconvenient. For example, this whole
 discussion is based on the idea that "if you don't have any inout
 parameters, why are you declaring inout variables? It makes no sense!" A
 very sensible and logical idea. But when you want code that works with
 or without inout variables, all of a sudden the same code is throwing an
 error in some cases because of an unforeseen situation.
This is an example of the mini-monster: the use of inout is necessary to satisfy the existence of inout.
 Another example is code like this:

 int firstInt(T...)(T t)
 {
    foreach(ref x; t)
    {
        static if(typeof(x) == int)
           return x;
    }
    return -1;
 }

 This throws an error because if there is an int in T..., then return -1
 statement is "unreachable". This is similar to the inout int = 0 case.
Not at all! There are very nice solutions for this, including looking at T or recursion. I think we can safely strike this one.
 3. For all problems that inout is purported to solve, I know of idioms
 that are definitely simpler and overall almost as good if not better. So
 a hard question is whether the existence is justified.
I know of no "better" idioms, in terms of code generation and specificity, to replace inout (or at least, inout as I envision it should be). Care to elaborate?
I thought I did. Use one-liner functions that forward to templates. Andrei
Apr 15 2016
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/15/16 3:28 PM, Andrei Alexandrescu wrote:
 On 4/15/16 2:39 PM, Steven Schveighoffer wrote:
 On 4/15/16 1:11 PM, Andrei Alexandrescu wrote:
 Would it be difficult to make it work without inout?
T[] dup(T)(T[] arr) this should be doable. Problem is, you get identical instantiations for each of the different qualifiers, unless you take different actions based on the mutability. Instead: inout(T)[] dup(T)(inout(T)[] arr) This is one instantiation, no matter the mutability of the parameter. And it guarantees no molestation of arr, even if it's mutable. This would be much more difficult with a struct/class, whereas with inout it's pretty simple. But I think dup as it is defined is a bad example, because as defined by the language, it implies that you want a mutable version where the source may be const/immutable. In the general sense, a duplication function where you get the *same* mutability is more applicable for inout.
A better support for this argument is std.array.replaceSlice at https://github.com/D-Programming-Language/phobos/blob/master/std/array.d#L2594: inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement); So here we are guaranteed that (a) the result type is the same as the first argument, and (b) the first argument is never modified even if a mutable slice is passed.
In fact, a mutable slice must be passed (slice is not marked as inout).
 I agree this is a nice property, and one that C++ does not enjoy. The
 questions are of course how important it is, how useful it is, and what
 price it is worth.

 Getting back to replaceSlice I'd define it in more flexible terms
 anyway. So I'd eliminate the use of inout there even if it did add value.
You can add more flexibility and keep inout at the same time. The nice feature here is that inout protects the elements of s that aren't in slice, and there is no requirement to verify s and slice are the same underlying type, etc.
 One thing to understand is that inout is only valuable if you think
 const is valuable. That is, you think the advertisement of "no, I will
 not modify this parameter, give me whatever you got" is of value. If you
 don't care, then templates suffice.
This is correct but incomplete. The omitted part is that inout is valuable when $EVERYTHING_YOU_SAID and also you want to transport the qualifier from an argument to the result. This diminishes its importance considerably for all folks, including those for whom const is important.
Nope. Actually inout has proven to have a more interesting property of not requiring casting.
 void popFront(T)(inout(T)[] arr)
void popFront(T)(ref inout(T)[] arr); (point stays)
Yes, thanks, that is what I meant.
 This is possible only with inout. const will not cut it. The current
 definition is:

 void popFront(T)(T[] arr)

 which is OK, but there is no advertisement that popFront doesn't change
 any data in arr.
Should this work as well? void popFront(ref const(T)[] arr); In other words conversion of ref T[] to ref const(T)[] is sound.
No, because it's not sound. you cannot cast to const through 2+ indirections. However inout works there.
 I think the point of Kenji's argument is that inout's current
 limitations are what you are bumping into, and those limitations are
 unnecessary and arbitrary. We can make inout better and more consistent.
 Pretty easily actually. We can certainly fix the inout int = 0 problem.
I'm not sure - at all in fact. This is at the tail of a sequence of changes of inout, and there is a history of such mini-designs leading to complications, regressions, and further mini-designs. It's the wolf that eats the dog that eats the cat that eats the mouse. We fix this and there's another and then another and then another. It has already happened.
I assure you, these limitations were self-imposed. I insisted on them, without realizing that they would cause problems with generic code. I thought they would be good "lint" detection. https://issues.dlang.org/show_bug.cgi?id=3748
 In fact Walter's and my design of inout is a prime example of the
 failings of that approach. We underdesigned it to work in a few small
 examples. We knew we lost when Kenji figured inout must be a type
 qualifier (we were hoping to getting away with it being a sort of a
 placeholder/wildcard). I think we should have pulled it back then. It's
 become a mini-monster that only wants more attention, more language
 changes, more work.
It must be a type qualifier or it doesn't work.
 An interesting thing is that when you involve generic code, certain
 "good ideas" become very inconvenient. For example, this whole
 discussion is based on the idea that "if you don't have any inout
 parameters, why are you declaring inout variables? It makes no sense!" A
 very sensible and logical idea. But when you want code that works with
 or without inout variables, all of a sudden the same code is throwing an
 error in some cases because of an unforeseen situation.
This is an example of the mini-monster: the use of inout is necessary to satisfy the existence of inout.
I don't know what this argument is saying?
 Another example is code like this:

 int firstInt(T...)(T t)
 {
    foreach(ref x; t)
    {
        static if(typeof(x) == int)
           return x;
    }
    return -1;
 }

 This throws an error because if there is an int in T..., then return -1
 statement is "unreachable". This is similar to the inout int = 0 case.
Not at all! There are very nice solutions for this, including looking at T or recursion. I think we can safely strike this one.
Absolutely similar. There are ways around it, but at the end of the day, the compiler is complaining that the line can never be reached, when in fact it can (just pass a short in). Just like there are ways around the inout local variable requirement.
 3. For all problems that inout is purported to solve, I know of idioms
 that are definitely simpler and overall almost as good if not better. So
 a hard question is whether the existence is justified.
I know of no "better" idioms, in terms of code generation and specificity, to replace inout (or at least, inout as I envision it should be). Care to elaborate?
I thought I did. Use one-liner functions that forward to templates.
I didn't see it? -Steve
Apr 15 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 03:44 PM, Steven Schveighoffer wrote:
 No, because it's not sound. you cannot cast to const through 2+
 indirections. However inout works there.
That is correct, a classic... thanks for getting me straight.
 I think the point of Kenji's argument is that inout's current
 limitations are what you are bumping into, and those limitations are
 unnecessary and arbitrary. We can make inout better and more consistent.
 Pretty easily actually. We can certainly fix the inout int = 0 problem.
I'm not sure - at all in fact. This is at the tail of a sequence of changes of inout, and there is a history of such mini-designs leading to complications, regressions, and further mini-designs. It's the wolf that eats the dog that eats the cat that eats the mouse. We fix this and there's another and then another and then another. It has already happened.
I assure you, these limitations were self-imposed. I insisted on them, without realizing that they would cause problems with generic code. I thought they would be good "lint" detection. https://issues.dlang.org/show_bug.cgi?id=3748
Problem is we could have other problems once we fix those. As you just showed, it has already happened. We should really do away with the cowboy style of designing language, which sadly Walter and I have been guilty of too often in the past. The slow but sure accretion of complexity of inout is a textbook example of where that leads. Andrei
Apr 15 2016
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/15/16 4:03 PM, Andrei Alexandrescu wrote:
 On 04/15/2016 03:44 PM, Steven Schveighoffer wrote:
 I assure you, these limitations were self-imposed. I insisted on them,
 without realizing that they would cause problems with generic code. I
 thought they would be good "lint" detection.

 https://issues.dlang.org/show_bug.cgi?id=3748
Problem is we could have other problems once we fix those. As you just showed, it has already happened.
If you look at the core of what inout actually is (a type modifier placeholder), we can simplify how to think about it, and how to implement it. These "requirements" were just extra helpful things that would flag valid, but (naively assumed) pointless code. Turns out, templates generate lots of pointless code (that is typically optimized away in the type system or the optimizer). I hope to make this clear in my talk at dconf.
 We should really do away with the cowboy style of designing language,
 which sadly Walter and I have been guilty of too often in the past. The
 slow but sure accretion of complexity of inout is a textbook example of
 where that leads.
I actually agree, someone with as little experience as I had should not have been listened to when making such a feature :) If I could do it over again, I would still want this feature, but obviously, we could make sane simple rules for it. They are actually quite easy to understand. -Steve
Apr 15 2016
prev sibling parent reply QAston <qaston gmail.com> writes:
On Friday, 15 April 2016 at 20:03:07 UTC, Andrei Alexandrescu 
wrote:
 We should really do away with the cowboy style of designing 
 language, which sadly Walter and I have been guilty of too 
 often in the past. The slow but sure accretion of complexity of 
 inout is a textbook example of where that leads.

 Andrei
This deserves to be a poster with a golden frame. You've got lucky with pure (modulo corner cases) and ctfe, much less lucky with safe, trusted, system, inout, shared, scope, property.
Apr 17 2016
parent reply Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On Sunday, 17 April 2016 at 14:30:59 UTC, QAston wrote:
 You've got lucky with pure (modulo corner cases) and ctfe, much 
 less lucky with  safe,  trusted,  system, inout, shared, scope, 
 property.
The safe troika is a good design (except safe should be the default), the implementation is lacking though. Ideallists want to make safe strict now, but break code sometimes even without basic workarounds for memory-safe code. Pragmatists want to avoid breakage but make the subset of safe code wider, making the definition more complex. There seems to be a stalemate. scope, if implemented for reference types, wouldn't scale well. It should be the default, with __escape meaning scope(false). I think it's an uphill battle arguing for this, but it is crucial to avoiding GC without runtime checks. At least for non-GC code in a general way. I think property is OK. I think the controversy at the time was about optional brackets in function calls, which is different.
Apr 17 2016
next sibling parent reply QAston <qaston gmail.com> writes:
On Sunday, 17 April 2016 at 16:44:50 UTC, Nick Treleaven wrote:
 The  safe troika is a good design (except  safe should be the 
 default), the implementation is lacking though. Ideallists want 
 to make  safe strict now, but break code sometimes even without 
 basic workarounds for memory-safe code. Pragmatists want to 
 avoid breakage but make the subset of  safe code wider, making 
 the definition more complex. There seems to be a stalemate.
Yeah, but for example rust deals with the same problem with a single keyword.
 scope, if implemented for reference types, wouldn't scale well. 
 It should be the default, with __escape meaning scope(false). I 
 think it's an uphill battle arguing for this, but it is crucial 
 to avoiding GC without runtime checks. At least for non-GC code 
 in a general way.
scope (for function parameters) cannot be implemented in backwards compatible way because a lot of code uses in (which is const^scope)
 I think  property is OK. I think the controversy at the time 
 was about optional brackets in function calls, which is 
 different.
First property + compiler switch, now property + deprecated switch. When should I use property? For all the getters? Should I start with property or with member access? Does it even matter because of optional parens? Why do I even need to care about this? Sure all of the things I've mentioned can be defended. The question is not whether they have a usecase (because all of them do), but whether return on investment for them is good enough and how they interact with the rest of the language. CTFE does what you'd expect it to do - constant folding is intuitive and requires little to no code changes. Purity is orthogonal to the rest of the language (modulo corner cases) and enables some patterns for immutable value creation. Those (and others - the stuff that's good doesn't come to mind because it works) have higher ROI and aren't as much of a burden for library writers.
Apr 17 2016
next sibling parent ag0aep6g <anonymous example.com> writes:
On 17.04.2016 19:39, QAston wrote:
 First  property + compiler switch, now  property + deprecated switch.
 When should I use property? For all the getters? Should I start with
 property or with member access? Does it even matter because of optional
 parens? Why do I even need to care about this?
property is in a bad state right now. The behavior that used to be enabled by -property has been decided against, and now property has only one effect that I can think of off the top of my head, and it's rather subtle: ---- int foo() {return 0;} property int bar() {return 0;} pragma(msg, typeof(foo)); /* int() */ pragma(msg, typeof(bar)); /* int */ pragma(msg, typeof(&foo)); /* int function() ref */ pragma(msg, typeof(&bar)); /* int function() property ref */ ---- I don't think anyone is really happy with the current property, but everyone is probably tired of discussing it.
Apr 17 2016
prev sibling parent Kagamin <spam here.lot> writes:
On Sunday, 17 April 2016 at 17:39:48 UTC, QAston wrote:
 First  property + compiler switch, now  property + deprecated 
 switch. When should I use property? For all the getters? Should 
 I start with property or with member access? Does it even 
 matter because of optional parens? Why do I even need to care 
 about this?
Rationale behind DIP23 seems to make language usable without property and provide a puristic feature for those who are into it and for one corner case.
Apr 19 2016
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 17.04.2016 18:44, Nick Treleaven wrote:
 I think  property is OK.
No, it isn't: import std.stdio; struct S{ property int delegate() foo(){ return ()=>3; } } void main(){ S s; writeln(s.foo()); // "int delegate()" }
Apr 17 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 17 April 2016 at 21:20:49 UTC, Timon Gehr wrote:
 On 17.04.2016 18:44, Nick Treleaven wrote:
 I think  property is OK.
No, it isn't:
Seriously, property is one of the biggest SNAFUs of the language. I think I'll write an editorial about this stuff in TWID tonight. (I'm also very skeptical of the value of pure, safe, nothrow, and nogc)
Apr 17 2016
next sibling parent reply Temtaime <temtaime gmail.com> writes:
On Sunday, 17 April 2016 at 23:03:26 UTC, Adam D. Ruppe wrote:
 On Sunday, 17 April 2016 at 21:20:49 UTC, Timon Gehr wrote:
 On 17.04.2016 18:44, Nick Treleaven wrote:
 I think  property is OK.
No, it isn't:
Seriously, property is one of the biggest SNAFUs of the language. I think I'll write an editorial about this stuff in TWID tonight. (I'm also very skeptical of the value of pure, safe, nothrow, and nogc)
nothrow may be useful for passing callbacks in C functions. shared currently is useless too tbw
Apr 17 2016
parent Kagamin <spam here.lot> writes:
On Monday, 18 April 2016 at 00:53:26 UTC, Temtaime wrote:
 shared currently is useless too tbw
shared did catch bugs in phobos and user code, that would otherwise sneak in.
Apr 19 2016
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
On Sunday, 17 April 2016 at 23:03:26 UTC, Adam D. Ruppe wrote:
 On Sunday, 17 April 2016 at 21:20:49 UTC, Timon Gehr wrote:
 On 17.04.2016 18:44, Nick Treleaven wrote:
 I think  property is OK.
No, it isn't:
Seriously, property is one of the biggest SNAFUs of the language.
Today I learned a new acronym. Fit property perfectly.
Apr 17 2016
prev sibling parent reply Guillaume Piolat <name.lastname gmail.com> writes:
On Sunday, 17 April 2016 at 23:03:26 UTC, Adam D. Ruppe wrote:
 On Sunday, 17 April 2016 at 21:20:49 UTC, Timon Gehr wrote:
 On 17.04.2016 18:44, Nick Treleaven wrote:
 I think  property is OK.
No, it isn't:
Seriously, property is one of the biggest SNAFUs of the language. I think I'll write an editorial about this stuff in TWID tonight. (I'm also very skeptical of the value of pure, safe, nothrow, and nogc)
As a big user of nogc, I'd disagree. nogc is a bit hard to use and effectively split the language in two, but gives the absolute confidence that nothing will block. In audio callbacks and you are supposed to use tryLock and atomics instead of locking, so allocating is a big problem. And it's very easy to let something passthrough like a rogue closure or an array literal. nogc is a big safety net I thought wasn't needed, until I had to make nogc code. Personnally I wish synchronized, comma operator, and actively harmful things would go. nothrow provides little value, but no negative value.
Apr 18 2016
parent reply Guillaume Piolat <name.lastname gmail.com> writes:
On Monday, 18 April 2016 at 08:52:19 UTC, Guillaume Piolat wrote:
 Personnally I wish synchronized, comma operator, and actively 
 harmful things would go. nothrow provides little value, but no 
 negative value.
And shared and property :) But I guess this isn't Christmas already.
Apr 18 2016
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 18 April 2016 at 08:53:31 UTC, Guillaume Piolat wrote:
 And shared and  property :)
I still want to see property fixed rather than removed - the edge case with returning a delegate is an interesting one to me (though that's ALL I want it to do, leave everything else alone)
Apr 18 2016
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
(It seems my reply got lost somewhere, reposting...)

On Friday, 15 April 2016 at 19:28:02 UTC, Andrei Alexandrescu 
wrote:
 A better support for this argument is std.array.replaceSlice at 
 https://github.com/D-Programming-Language/phobos/blob/master/std/array.d#L2594:

 inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] 
 replacement);

 So here we are guaranteed that (a) the result type is the same 
 as the first argument, and (b) the first argument is never 
 modified even if a mutable slice is passed.
What are the plans for DIP25's `return` attribute? Because with it, the compiler has enough information to know that the return value aliases `s`: const(T)[] replaceSlice(T)(const(T)[] s return, in T[] slice, in T[] replacement); If the function is passed a mutable `s`, its return value can be implicitly convertible to `T[]`.
Apr 16 2016
parent reply Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On 16/04/2016 12:40, Marc Schütz wrote:
 What are the plans for DIP25's `return` attribute? Because with it, the
 compiler has enough information to know that the return value aliases `s`:

 const(T)[] replaceSlice(T)(const(T)[] s return, in T[] slice, in T[]
 replacement);

 If the function is passed a mutable `s`, its return value can be
 implicitly convertible to `T[]`.
AIUI, functions don't have to return part of the parameter tagged with return, it can return anything. See: http://wiki.dlang.org/DIP25#Types_of_Result_vs._Parameters
Apr 16 2016
parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Saturday, 16 April 2016 at 11:49:21 UTC, Nick Treleaven wrote:
 On 16/04/2016 12:40, Marc Schütz wrote:
 What are the plans for DIP25's `return` attribute? Because 
 with it, the
 compiler has enough information to know that the return value 
 aliases `s`:

 const(T)[] replaceSlice(T)(const(T)[] s return, in T[] slice, 
 in T[]
 replacement);

 If the function is passed a mutable `s`, its return value can 
 be
 implicitly convertible to `T[]`.
AIUI, functions don't have to return part of the parameter tagged with return, it can return anything. See: http://wiki.dlang.org/DIP25#Types_of_Result_vs._Parameters
I'm not sure. That section says that the situation may change in the future. Other parts of the DIP can be read both ways, but it doesn't mention aliasing explicitly. As this is currently still experimental and not a complete design anyway, we can change it to fit our needs.
Apr 18 2016
prev sibling next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 15-Apr-2016 08:38, Andrei Alexandrescu wrote:
 On 04/15/2016 12:23 AM, Jonathan M Davis via Digitalmars-d wrote:
 On Thursday, April 14, 2016 23:10:12 Andrei Alexandrescu via
 Digitalmars-d
 wrote:
[snip]
 - Jonathan M Davis
I think we should deprecate inout. For real. It costs way too much for what it does. For all I can tell most of D's proponents don't know how it works. -- Andrei
Love this trend! inout is a monstrously complicated feature solving minor inconvenience. -- Dmitry Olshansky
Apr 15 2016
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2016-04-15 07:38, Andrei Alexandrescu wrote:

 I think we should deprecate inout. For real. It costs way too much for
 what it does. For all I can tell most of D's proponents don't know how
 it works. -- Andrei
If "inout" is only used as a way avoid code duplication, both when writing the code and the compiler generating the code. Then that can be solved with two steps: 1. Improve the compiler to remove duplicated functions overloaded on constness. That is, they generate the exact same code and and the only difference is the constness of the functions. This is a useful improvement regardless 2. Write a string mixin that duplicates a function three times, one for each type of constness: // Assuming "inout" is removed from the language and not a keyword anymore mixin(inout(q{ inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement) { ... } }); The "input" function would need to parse arbitrary D code and create three version of the passed in function declaration, mutable, const and immutable. In theory libdparse could be used for this but it doesn't work at compile time. The downside is that it looks really ugly when defining an inout function. But, with AST macros it could look like this: inout inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement); -- /Jacob Carlborg
Apr 16 2016
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On Friday, 15 April 2016 at 04:23:29 UTC, Jonathan M Davis wrote:
 IIRC, the problem has to do with ranges of inout elements 
 working correctly, which gets really funky, because inout is a 
 temporary thing and not a full-on type constructor/qualifier. I 
 believe that Kenji is the one that implemented the fix, and I 
 think that he explained it in the newsgroup at some point. 
 Certainly, there have been a few times that it's come up in 
 D.Learn when folks ask what the heck it is, so there should be 
 a few posts floating around with an explanation. This is the 
 only useful post that I could find in a quick search though:
Whatever the problem is, none of this seems like a good solution.
Apr 15 2016
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 03:42 AM, deadalnix wrote:
 On Friday, 15 April 2016 at 04:23:29 UTC, Jonathan M Davis wrote:
 IIRC, the problem has to do with ranges of inout elements working
 correctly, which gets really funky, because inout is a temporary thing
 and not a full-on type constructor/qualifier. I believe that Kenji is
 the one that implemented the fix, and I think that he explained it in
 the newsgroup at some point. Certainly, there have been a few times
 that it's come up in D.Learn when folks ask what the heck it is, so
 there should be a few posts floating around with an explanation. This
 is the only useful post that I could find in a quick search though:
Whatever the problem is, none of this seems like a good solution.
I agree. -- Andrei
Apr 15 2016
prev sibling parent reply Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On Friday, 15 April 2016 at 04:23:29 UTC, Jonathan M Davis wrote:
 Certainly, there have been a few times that it's come up in 
 D.Learn when folks ask what the heck it is, so there should be 
 a few posts floating around with an explanation. This is the 
 only useful post that I could find in a quick search though:

 http://forum.dlang.org/post/mh68p8$2p56$1 digitalmars.com
He says:
 Alternatively, you could have the lambda take an R as a 
 parameter. Or fix the semantics ...
Assuming it works OK, that seems much cleaner: (R r) { if (r.empty) {} ... }
Apr 15 2016
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 4/15/16 8:59 AM, Nick Treleaven wrote:
 On Friday, 15 April 2016 at 04:23:29 UTC, Jonathan M Davis wrote:
 Certainly, there have been a few times that it's come up in D.Learn
 when folks ask what the heck it is, so there should be a few posts
 floating around with an explanation. This is the only useful post that
 I could find in a quick search though:

 http://forum.dlang.org/post/mh68p8$2p56$1 digitalmars.com
He says:
 Alternatively, you could have the lambda take an R as a parameter. Or
 fix the semantics ...
Assuming it works OK, that seems much cleaner: (R r) { if (r.empty) {} ... }
That makes other unittests fail (those with noncopyable ranges). Overall we really need to spend some time on making Phobos simpler and better. The mishmash of styles and variable quality of contributions has slowly been gnawing at the sheer quality of the code. Andrei
Apr 15 2016
prev sibling next sibling parent reply Shammah Chancellor <shammah.chancellor gmail.com> writes:
On Friday, 15 April 2016 at 03:10:12 UTC, Andrei Alexandrescu 
wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152

 There is no explanation to it in the source code, and the line 
 blames to 
 https://github.com/D-Programming-Language/phobos/pull/2661 
 (irrelevant).

 Commenting it out yields a number of unittest compilation 
 errors, neither informative about the root of the problem and 
 indicative as to how the parameter solves it.

 There are two issues here:

 1. Once a problem/solution pair of this degree of subtlety 
 crops up, we need to convince ourselves that that's sensible. 
 If we deem it not so, we look into improving the language to 
 make the problem disappear.

 2. There needs to be documentation for people working on the 
 standard library so they don't need to waste time on their own 
 discovery process.

 We want Phobos to be beautiful, a prime example of good D code. 
 Admittedly, it also needs to be very general and efficient, 
 which sometimes gets in the way. But we cannot afford an 
 accumulation of mad tricks to obscure the large design.


 Andrei
`(int inout = 0)` is not the only problem with that template -- and it's bothered me for years. `is(typeof( () { ... } ) )` as a whole looks like a "trick" to me. It's not going to be immediately obvious to someone who reads the D spec, and then that code, what that template does. I think that behavioral type checks are common enough in D that it should have it's own first-class syntax. -Shammah
Apr 14 2016
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 02:40 AM, Shammah Chancellor wrote:
 I think that behavioral type checks are common enough in D that it
 should have it's own first-class syntax.
No. -- Andrei
Apr 15 2016
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/14/2016 8:10 PM, Andrei Alexandrescu wrote:
 Commenting it out yields a number of unittest compilation errors, neither
 informative about the root of the problem and indicative as to how the
parameter
 solves it.
https://issues.dlang.org/show_bug.cgi?id=15926
Apr 14 2016
next sibling parent reply w0rp <devw0rp gmail.com> writes:
I think it has something to do with making the function, in this 
case a lambda, inout, so that it can accept inout types. Then the 
typeof bit is a weird way to writing something like 
__traits(compiles, ...) , because functions which have no type 
result in void, and that fails the typeof check.

If we do end up replacing inout with something else, I would like 
something which solves the problem of declaring functions 
returning ranges of either mutable, const, or immutable. I've 
struggled with that before: 
https://github.com/w0rp/dstruct/blob/master/source/dstruct/graph.d#L628
Apr 15 2016
parent w0rp <devw0rp gmail.com> writes:
On Friday, 15 April 2016 at 07:33:42 UTC, w0rp wrote:
 I think it has something to do with making the function, in 
 this case a lambda, inout, so that it can accept inout types. 
 Then the typeof bit is a weird way to writing something like 
 __traits(compiles, ...) , because functions which have no type 
 result in void, and that fails the typeof check.

 If we do end up replacing inout with something else, I would 
 like something which solves the problem of declaring functions 
 returning ranges of either mutable, const, or immutable. I've 
 struggled with that before: 
 https://github.com/w0rp/dstruct/blob/master/source/dstruct/graph.d#L628
To clarify my example. My problem was that I had a container which was immutable, and I wanted a range over the immutable elements, where the range itself is of course mutable. Getting that to work was a tad tricky. You can't take an inout() container and return an inout() range, because then an immutable container will produce an immutable range, which isn't useful. I don't think you can return a mutable range containing inout() elements either.
Apr 15 2016
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 02:49 AM, Walter Bright wrote:
 On 4/14/2016 8:10 PM, Andrei Alexandrescu wrote:
 Commenting it out yields a number of unittest compilation errors, neither
 informative about the root of the problem and indicative as to how the
 parameter
 solves it.
https://issues.dlang.org/show_bug.cgi?id=15926
Nice, thanks. I think I can put a finger on what bothers me the most: adding that "inout int = 0" to make code work cannot be an acceptable solution to any problem. So the fact that the problem exists is the actual problem. It's basic sense and sensibility in engineering. -- Andrei
Apr 15 2016
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 15.04.2016 05:10, Andrei Alexandrescu wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152
 ...
Related: Phobos should never use is(typeof(...)). Contrary to popular belief, is(typeof(...)) is not the same as __traits(compiles,...). void main(){ int x; static void foo(){ static assert(is(typeof({return x;}))); static assert(!__traits(compiles,{return x;})); } } typeof suspends context access checks. This is almost never what you want when checking for compilability.
 There is no explanation to it in the source code, and the line blames to
 https://github.com/D-Programming-Language/phobos/pull/2661 (irrelevant).

 Commenting it out yields a number of unittest compilation errors,
 neither informative about the root of the problem and indicative as to
 how the parameter solves it.

 There are two issues here:

 1. Once a problem/solution pair of this degree of subtlety crops up, we
 need to convince ourselves that that's sensible. If we deem it not so,
 we look into improving the language to make the problem disappear.
 ...
Declaring variables with inout in their type is only allowed for stack variables in functions that have inout parameters. The (inout int) trick attempts to allow such types to be ranges. It is not even a correct fix: Most of Phobos code assumes that ranges be struct fields. The following code causes a compilation failure in the guts of std.algorithm: inout(int)[] foo(inout(int)[] x){ static assert(isInputRange!(typeof(x))); return x.map!(x=>x).array; } What good is an "InputRange" that does not support map? The fundamental problem is that inout is disallows certain kinds of composition. It's a flawed language primitive. The way to fix this is to have parametric polymorphism in the language.
Apr 15 2016
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 15.04.2016 11:07, Timon Gehr wrote:
 ... Most of Phobos code assumes that ranges be struct fields.
Most of Phobos assumes that ranges can be
 The fundamental problem is that inout is disallows certain kinds of
 composition. ...
inout disallows
Apr 15 2016
prev sibling next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 15 April 2016 at 09:07:27 UTC, Timon Gehr wrote:
 Related: Phobos should never use is(typeof(...)). Contrary to 
 popular belief, is(typeof(...)) is not the same as 
 __traits(compiles,...).
I don't think it should be using __traits(compiles) either. I'd prefer to see built from the other reflection primitives... though I'll grant that compiles is flexible. hasMember!(Range, "empty") isn't as specific as the current test... and is(typeof(Range.init.empty) : bool) isn't as flexible (I don't think that takes an empty that returns an opCast:bool item) But regardless, I still kinda prefer the idea of building these reflection helper functions so they do cover the cases nicely... then a helper function that takes a list of conditions and tells you which one failed... hmm, this is getting interesting.
Apr 15 2016
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 05:07 AM, Timon Gehr wrote:
 The fundamental problem is that inout is disallows certain kinds of
 composition. It's a flawed language primitive.
I agree. The more I try things with it the more awfully complex and useless it is. inout must go. -- Andrei
Apr 15 2016
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 15 Apr 2016 09:44:05 -0400
schrieb Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:

inout must go. -- Andrei
 
Ceterum censeo Carthaginem esse delendam. -- Marcus Porcius Cato
:o)

-- 
Marco
Apr 16 2016
parent Lass Safin <lasssafin gmail.com> writes:
On Saturday, 16 April 2016 at 22:06:10 UTC, Marco Leise wrote:
 Am Fri, 15 Apr 2016 09:44:05 -0400
 schrieb Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:

 inout must go. -- Andrei

 Ceterum censeo Carthaginem esse delendam. -- Marcus Porcius Cato
 :o)
What does that have to do with what he said? Are you comparing him to Cato?
Apr 16 2016
prev sibling next sibling parent reply ixid <adamsibson hotmail.com> writes:
On Friday, 15 April 2016 at 03:10:12 UTC, Andrei Alexandrescu 
wrote:
 We want Phobos to be beautiful, a prime example of good D code. 
 Admittedly, it also needs to be very general and efficient, 
 which sometimes gets in the way. But we cannot afford an 
 accumulation of mad tricks to obscure the large design.


 Andrei
Why didn't we go with all functions being able to infer const, pure etc rather than just templates?
Apr 15 2016
next sibling parent Guillaume Piolat <contact gam3sfrommars.fr> writes:
On Friday, 15 April 2016 at 12:54:11 UTC, ixid wrote:
 Why didn't we go with all functions being able to infer const, 
 pure etc rather than just templates?
Non-templated function may not have their source code available. For consistency, non-template function have no inference.
Apr 15 2016
prev sibling parent deadalnix <deadalnix gmail.com> writes:
On Friday, 15 April 2016 at 12:54:11 UTC, ixid wrote:
 On Friday, 15 April 2016 at 03:10:12 UTC, Andrei Alexandrescu 
 wrote:
 We want Phobos to be beautiful, a prime example of good D 
 code. Admittedly, it also needs to be very general and 
 efficient, which sometimes gets in the way. But we cannot 
 afford an accumulation of mad tricks to obscure the large 
 design.


 Andrei
Why didn't we go with all functions being able to infer const, pure etc rather than just templates?
The problem is essentially untractable when there are loops in the call graph. That being said, it would make sense to do it for auto functions.
Apr 15 2016
prev sibling next sibling parent QAston <qaston gmail.com> writes:
On Friday, 15 April 2016 at 03:10:12 UTC, Andrei Alexandrescu 
wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152

 There is no explanation to it in the source code, and the line 
 blames to 
 https://github.com/D-Programming-Language/phobos/pull/2661 
 (irrelevant).
I've been wondering what that is myself, so I've just cargo-cult-copied it into my code. Thanks for the post.
Apr 15 2016
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/14/16 11:10 PM, Andrei Alexandrescu wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152
It works around a limitation of inout that isn't necessary (even though I thought it was being helpful when I suggested it). That is, functions without inout parameters cannot declare local inout variables. But this isn't really necessary, and should be fixed. I will discuss this in my talk in a few weeks. Note, the =0 part isn't necessary right now, since it's not called. It's just used to test if the function can compile. In short, my opinion on inout is that it has some unnecessary limitations, which can be removed, and inout will work as mostly expected. These requirements to work around the limitations will go away. I expect after reading this thread that the Q&A will be.. interesting. -Steve
Apr 15 2016
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 15.04.2016 17:22, Steven Schveighoffer wrote:
 On 4/14/16 11:10 PM, Andrei Alexandrescu wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152
It works around a limitation of inout that isn't necessary (even though I thought it was being helpful when I suggested it). That is, functions without inout parameters cannot declare local inout variables. But this isn't really necessary, and should be fixed. I will discuss this in my talk in a few weeks. ...
That's potentially dangerous. What about cases like the following? void main(){ inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); int[] z=foo(b); }
 Note, the =0 part isn't necessary right now, since it's not called. It's
 just used to test if the function can compile.

 In short, my opinion on inout is that it has some unnecessary
 limitations, which can be removed, and inout will work as mostly
 expected. These requirements to work around the limitations will go away.
 ...
Other important limitations of inout are e.g.: - inout variables cannot be fields. - There can be only one inout in scope.
 I expect after reading this thread that the Q&A will be.. interesting.

 -Steve
Apr 15 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/15/16 3:48 PM, Timon Gehr wrote:
 On 15.04.2016 17:22, Steven Schveighoffer wrote:
 On 4/14/16 11:10 PM, Andrei Alexandrescu wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152
It works around a limitation of inout that isn't necessary (even though I thought it was being helpful when I suggested it). That is, functions without inout parameters cannot declare local inout variables. But this isn't really necessary, and should be fixed. I will discuss this in my talk in a few weeks. ...
That's potentially dangerous. What about cases like the following? void main(){ inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); int[] z=foo(b); }
We don't need to guess: void foo (inout int) { inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); // line 9 int[] z=foo(b); // line 10 } testinout.d(9): Error: modify inout to immutable is not allowed inside inout function testinout.d(10): Error: modify inout to mutable is not allowed inside inout function
 Note, the =0 part isn't necessary right now, since it's not called. It's
 just used to test if the function can compile.

 In short, my opinion on inout is that it has some unnecessary
 limitations, which can be removed, and inout will work as mostly
 expected. These requirements to work around the limitations will go away.
 ...
Other important limitations of inout are e.g.: - inout variables cannot be fields.
I have a way to make this work. This is actually the most major sticking point in inout. The only correct thing is to keep is that globals/static variables cannot be typed inout.
 - There can be only one inout in scope.
This is not so much a problem I think. -Steve
Apr 15 2016
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 04:03 PM, Steven Schveighoffer wrote:
 I have a way to make this work. This is actually the most major sticking
 point in inout.

 The only correct thing is to keep is that globals/static variables
 cannot be typed inout.
Another special case? The only correct thing is to simplify the language to everybody's benefit. -- Andrei
Apr 15 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/15/16 4:08 PM, Andrei Alexandrescu wrote:
 On 04/15/2016 04:03 PM, Steven Schveighoffer wrote:
 I have a way to make this work. This is actually the most major sticking
 point in inout.

 The only correct thing is to keep is that globals/static variables
 cannot be typed inout.
Another special case? The only correct thing is to simplify the language to everybody's benefit. -- Andrei
This is not a special case any more than disallowing access of shared data from a pure function is a special case. In fact, you could allow inout variables as static or globals, but just couldn't copy them to local inout variables (except full value types). The point is that between 2 different calls to inout, the wrapper means something different. A global/static persists between calls. -Steve
Apr 15 2016
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 15.04.2016 22:03, Steven Schveighoffer wrote:
 On 4/15/16 3:48 PM, Timon Gehr wrote:
 On 15.04.2016 17:22, Steven Schveighoffer wrote:
 On 4/14/16 11:10 PM, Andrei Alexandrescu wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152
It works around a limitation of inout that isn't necessary (even though I thought it was being helpful when I suggested it). That is, functions without inout parameters cannot declare local inout variables. But this isn't really necessary, and should be fixed. I will discuss this in my talk in a few weeks. ...
That's potentially dangerous. What about cases like the following? void main(){ inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); int[] z=foo(b); }
We don't need to guess: void foo (inout int) { inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); // line 9 int[] z=foo(b); // line 10 } testinout.d(9): Error: modify inout to immutable is not allowed inside inout function testinout.d(10): Error: modify inout to mutable is not allowed inside inout function
I'm very aware of that: https://issues.dlang.org/show_bug.cgi?id=10758 main is not an inout function in the example above. I.e. if we just change the compiler minimally such as to allow making inout local variables, the above example will violate immutability guarantees. You can also imagine cases where the inout local variable is defined only after the local inout function has been declared and called with a non-inout argument. At which point does the constraint become active? etc. We cannot _simply_ allow declaring inout locals.
 Note, the =0 part isn't necessary right now, since it's not called. It's
 just used to test if the function can compile.

 In short, my opinion on inout is that it has some unnecessary
 limitations, which can be removed, and inout will work as mostly
 expected. These requirements to work around the limitations will go
 away.
 ...
Other important limitations of inout are e.g.: - inout variables cannot be fields.
I have a way to make this work.
Without syntax changes? Can the struct/class instances with inout fields be returned from the enclosing inout function?
 This is actually the most major sticking
 point in inout.
  The only correct thing is to keep is that globals/static variables
 cannot be typed inout.

 - There can be only one inout in scope.
This is not so much a problem I think. ...
I think it is. It's just not the prevalent limitation one runs in at the moment. It will be more of a problem once functions can return structs with inout fields. IMHO compositionality should be ensured for a language feature from the start.
Apr 15 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/15/16 4:27 PM, Timon Gehr wrote:
 On 15.04.2016 22:03, Steven Schveighoffer wrote:
 On 4/15/16 3:48 PM, Timon Gehr wrote:
 On 15.04.2016 17:22, Steven Schveighoffer wrote:
 On 4/14/16 11:10 PM, Andrei Alexandrescu wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152
It works around a limitation of inout that isn't necessary (even though I thought it was being helpful when I suggested it). That is, functions without inout parameters cannot declare local inout variables. But this isn't really necessary, and should be fixed. I will discuss this in my talk in a few weeks. ...
That's potentially dangerous. What about cases like the following? void main(){ inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); int[] z=foo(b); }
We don't need to guess: void foo (inout int) { inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); // line 9 int[] z=foo(b); // line 10 } testinout.d(9): Error: modify inout to immutable is not allowed inside inout function testinout.d(10): Error: modify inout to mutable is not allowed inside inout function
I'm very aware of that: https://issues.dlang.org/show_bug.cgi?id=10758 main is not an inout function in the example above. I.e. if we just change the compiler minimally such as to allow making inout local variables, the above example will violate immutability guarantees. You can also imagine cases where the inout local variable is defined only after the local inout function has been declared and called with a non-inout argument. At which point does the constraint become active? etc. We cannot _simply_ allow declaring inout locals.
There's no difference between a function that declares its variables inout within its parameters or one that declares them locally. They should be treated the same once the function starts compiling.
 Note, the =0 part isn't necessary right now, since it's not called.
 It's
 just used to test if the function can compile.

 In short, my opinion on inout is that it has some unnecessary
 limitations, which can be removed, and inout will work as mostly
 expected. These requirements to work around the limitations will go
 away.
 ...
Other important limitations of inout are e.g.: - inout variables cannot be fields.
I have a way to make this work.
Without syntax changes? Can the struct/class instances with inout fields be returned from the enclosing inout function?
Yes, as long as inout is wrapping inout. We run into this currently with inout functions that create local types. e.g. emplace. Obviously inout cannot be unwrapped if it is typed on a field of a struct or class. So the unwrapping has to result in inout.
 - There can be only one inout in scope.
This is not so much a problem I think. ...
I think it is. It's just not the prevalent limitation one runs in at the moment. It will be more of a problem once functions can return structs with inout fields. IMHO compositionality should be ensured for a language feature from the start.
At the point where we need to tag multiple pools of inout parameters, the complexity of the language doesn't justify the benefits. We could make it possible, for instance, to templatize the mutability modifier instead of using a specific keyword. Then you could have foo(int)[]. Then I think you could do all this (and scrap inout), but I wouldn't want to work in that language. -Steve
Apr 15 2016
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 04:47 PM, Steven Schveighoffer wrote:
 There's no difference between a function that declares its variables
 inout within its parameters or one that declares them locally.
So now we get to things like: void fun() { inout int ohHello = 42; ... } How to explain such a construct? Not to mention globals of that type are not allowed. Andrei
Apr 15 2016
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 15.04.2016 23:03, Andrei Alexandrescu wrote:
 On 04/15/2016 04:47 PM, Steven Schveighoffer wrote:
 There's no difference between a function that declares its variables
 inout within its parameters or one that declares them locally.
So now we get to things like: void fun() { inout int ohHello = 42; ... } How to explain such a construct? Not to mention globals of that type are not allowed. Andrei
It's an int that has not decided yet whether it wants to be mutable, const or immutable and goes out of scope before it is able to make up its mind.
Apr 15 2016
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 15.04.2016 22:47, Steven Schveighoffer wrote:
 On 4/15/16 4:27 PM, Timon Gehr wrote:
 On 15.04.2016 22:03, Steven Schveighoffer wrote:
 On 4/15/16 3:48 PM, Timon Gehr wrote:
 On 15.04.2016 17:22, Steven Schveighoffer wrote:
 On 4/14/16 11:10 PM, Andrei Alexandrescu wrote:
 Consider:

 https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L152
It works around a limitation of inout that isn't necessary (even though I thought it was being helpful when I suggested it). That is, functions without inout parameters cannot declare local inout variables. But this isn't really necessary, and should be fixed. I will discuss this in my talk in a few weeks. ...
That's potentially dangerous. What about cases like the following? void main(){ inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); int[] z=foo(b); }
We don't need to guess: void foo (inout int) { inout(int)[] x=[1,2,3]; immutable(int) a; int b; inout(int)[] foo(inout int){ return x; } immutable(int)[] y=foo(a); // line 9 int[] z=foo(b); // line 10 } testinout.d(9): Error: modify inout to immutable is not allowed inside inout function testinout.d(10): Error: modify inout to mutable is not allowed inside inout function
I'm very aware of that: https://issues.dlang.org/show_bug.cgi?id=10758 main is not an inout function in the example above. I.e. if we just change the compiler minimally such as to allow making inout local variables, the above example will violate immutability guarantees. You can also imagine cases where the inout local variable is defined only after the local inout function has been declared and called with a non-inout argument. At which point does the constraint become active? etc. We cannot _simply_ allow declaring inout locals.
There's no difference between a function that declares its variables inout within its parameters or one that declares them locally. ...
Yes, there is. Semantic analysis sees the parameter types before it sees the body.
 They should be treated the same once the function starts compiling.
 ...
I think I have stated clearly why this is impossible. :P
 Note, the =0 part isn't necessary right now, since it's not called.
 It's
 just used to test if the function can compile.

 In short, my opinion on inout is that it has some unnecessary
 limitations, which can be removed, and inout will work as mostly
 expected. These requirements to work around the limitations will go
 away.
 ...
Other important limitations of inout are e.g.: - inout variables cannot be fields.
I have a way to make this work.
Without syntax changes? Can the struct/class instances with inout fields be returned from the enclosing inout function?
Yes, as long as inout is wrapping inout. We run into this currently with inout functions that create local types. e.g. emplace. Obviously inout cannot be unwrapped if it is typed on a field of a struct or class. So the unwrapping has to result in inout. ...
Ok.
 - There can be only one inout in scope.
This is not so much a problem I think. ...
I think it is. It's just not the prevalent limitation one runs in at the moment. It will be more of a problem once functions can return structs with inout fields. IMHO compositionality should be ensured for a language feature from the start.
At the point where we need to tag multiple pools of inout parameters, the complexity of the language doesn't justify the benefits. ...
I think this is a funny place to draw the line, but I guess this is a matter of taste.
 We could make it possible, for instance, to templatize the mutability
 modifier instead of using a specific keyword. Then you could have
 foo(int)[]. Then I think you could do all this (and scrap inout), but I
 wouldn't want to work in that language.

 -Steve
Well, that is precisely the way that languages with real type systems address issues like this one. D has many others like it. Note that for type systems, complexity and expressiveness do not necessarily correlate.
Apr 15 2016
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 04/15/2016 05:17 PM, Timon Gehr wrote:
 Well, that is precisely the way that languages with real type systems
 address issues like this one. D has many others like it.

 Note that for type systems, complexity and expressiveness do not
 necessarily correlate.
Nicely put on both counts. (Well "real" is semantically sarcastic a bit.) I don't even disagree :o). -- Andrei
Apr 15 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/15/16 5:17 PM, Timon Gehr wrote:
 On 15.04.2016 22:47, Steven Schveighoffer wrote:
 There's no difference between a function that declares its variables
 inout within its parameters or one that declares them locally.
 ...
Yes, there is. Semantic analysis sees the parameter types before it sees the body.
I don't know what the current implementation sees the function as doing. The way I look at it, the function context is like an extra parameter to the inner function. If it contains inout data, then that needs to be taken into account if the inner function has additional inout parameters. For example: inout(int) *x; inout(int) *foo(inout int) {return x;} foo is really taking 2 parameters: y and the context pointer that contains x. It almost looks like this: inout(int) *foo(inout int, ref inout(int) *x) { return x;} call this with: foo(1, x) And it won't compile. However, call it with: foo(inout(int)(1), x); and it should be fine, returning an inout(int)*.
 They should be treated the same once the function starts compiling.
 ...
I think I have stated clearly why this is impossible. :P
Impossible or difficult to do with the current implementation?
 At the point where we need to tag multiple pools of inout parameters,
 the complexity of the language doesn't justify the benefits.
 ...
I think this is a funny place to draw the line, but I guess this is a matter of taste.
I may have said this incorrectly. The language itself wouldn't really be that much more complex. It's the cost of understanding what each of the different inout pools mean. The benefit would be quite small, whereas there are obvious places inout makes sense -- the 'this' parameter and the return value. Then there is the syntax that would be required, I'm not sure what that looks like.
 We could make it possible, for instance, to templatize the mutability
 modifier instead of using a specific keyword. Then you could have
 foo(int)[]. Then I think you could do all this (and scrap inout), but I
 wouldn't want to work in that language.
Well, that is precisely the way that languages with real type systems address issues like this one. D has many others like it.
Aye, solutions like Rebindable, which is pretty much a failure IMO, show how lack of expressiveness in the core language can't be easily substituted.
 Note that for type systems, complexity and expressiveness do not
 necessarily correlate.
Humans are creatures of habit and familiarity. To allow each library to define what words mean what for modifiers would be really difficult to deal with. -Steve
Apr 15 2016
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 15.04.2016 23:56, Steven Schveighoffer wrote:
 On 4/15/16 5:17 PM, Timon Gehr wrote:
 On 15.04.2016 22:47, Steven Schveighoffer wrote:
 There's no difference between a function that declares its variables
 inout within its parameters or one that declares them locally.
 ...
Yes, there is. Semantic analysis sees the parameter types before it sees the body.
I don't know what the current implementation sees the function as doing. The way I look at it, the function context is like an extra parameter to the inner function. If it contains inout data,
Rather, when the inout data is actually accessed.
 then that needs to be
 taken into account if the inner function has additional inout parameters.
 ...
That's sensible, of course. The current implementation is a lot more conservative though.
 For example:

 inout(int) *x;

 inout(int) *foo(inout int) {return x;}

 foo is really taking 2 parameters: y and the context pointer that
 contains x. It almost looks like this:

 inout(int) *foo(inout int, ref inout(int) *x) { return x;}

 call this with: foo(1, x)

 And it won't compile.

 However, call it with: foo(inout(int)(1), x);

 and it should be fine, returning an inout(int)*.

 They should be treated the same once the function starts compiling.
 ...
I think I have stated clearly why this is impossible. :P
Impossible or difficult to do with the current implementation? ...
What I'm saying is that the check that is currently implemented is not adequate for the new case. This is not terribly important though. The point was that there needs to be a design effort beyond lifting the limitation, and you seemed to claim that the current implementation already takes care of the presented issue, which is not the case. Your ideas are sound though.
 At the point where we need to tag multiple pools of inout parameters,
 the complexity of the language doesn't justify the benefits.
 ...
I think this is a funny place to draw the line, but I guess this is a matter of taste.
I may have said this incorrectly. The language itself wouldn't really be that much more complex. It's the cost of understanding what each of the different inout pools mean.
They would just be named parameters to the function on the type system level, similar to template arguments but not causing repeated instantiation.
 The benefit would be quite small, whereas
 there are obvious places inout makes sense -- the 'this' parameter and
 the return value.
 ...
The return value might contain more than one pointer, and functions often have more than one parameter.
 Then there is the syntax that would be required, I'm not sure what that
 looks like.
 ...
Anything that is analogous to template parameters, e.g. an additional set of arguments with a different delimiter. Many workable possibilities. Anyway, it is unlikely to happen.
 We could make it possible, for instance, to templatize the mutability
 modifier instead of using a specific keyword. Then you could have
 foo(int)[]. Then I think you could do all this (and scrap inout), but I
 wouldn't want to work in that language.
Well, that is precisely the way that languages with real type systems address issues like this one. D has many others like it.
Aye, solutions like Rebindable, which is pretty much a failure IMO, show how lack of expressiveness in the core language can't be easily substituted.
 Note that for type systems, complexity and expressiveness do not
 necessarily correlate.
Humans are creatures of habit and familiarity. To allow each library to define what words mean what for modifiers would be really difficult to deal with. -Steve
I don't see how the library would do that.
Apr 15 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/15/16 6:31 PM, Timon Gehr wrote:
 On 15.04.2016 23:56, Steven Schveighoffer wrote:
 Impossible or difficult to do with the current implementation?
 ...
What I'm saying is that the check that is currently implemented is not adequate for the new case. This is not terribly important though. The point was that there needs to be a design effort beyond lifting the limitation, and you seemed to claim that the current implementation already takes care of the presented issue, which is not the case. Your ideas are sound though.
I'm sorry, should have put on my standard disclaimer that I am not a compiler writer :) I actually have no idea how this is done in the compiler, just how a compiler should behave as I understand it in my head.
 I may have said this incorrectly. The language itself wouldn't really be
 that much more complex. It's the cost of understanding what each of the
 different inout pools mean.
They would just be named parameters to the function on the type system level, similar to template arguments but not causing repeated instantiation.
Right, but again: these are named by the writer of the template, not the language, right? In that case, any identifier becomes a modifier. Or are you thinking of something different?
 The benefit would be quite small, whereas
 there are obvious places inout makes sense -- the 'this' parameter and
 the return value.
 ...
The return value might contain more than one pointer, and functions often have more than one parameter.
Of course, but the transference of mutability from parameter to return value is the obvious draw of such a wildcard. In most cases, there is only one return value, and therefore only one pool that needs to be handled. ref/out parameters can serve as alternate returns as well as composed types. It's not that functions don't have multiple returns with their own inout, but I don't think it's very common. As we get more and more obscure, the cost/benefit ratio for complexity vs. power gets higher. And already people don't like where we are right now with it.
 Then there is the syntax that would be required, I'm not sure what that
 looks like.
 ...
Anything that is analogous to template parameters, e.g. an additional set of arguments with a different delimiter. Many workable possibilities. Anyway, it is unlikely to happen.
Especially given the discussion happening here, I agree.
 Humans are creatures of habit and familiarity. To allow each library to
 define what words mean what for modifiers would be really difficult to
 deal with.
I don't see how the library would do that.
Just throwing a strawman out there: void foo(bingo = inout, zingo = inout)(bingo(int)* x, zingo(int)* y) So inside foo, bingo is one flavor of inout, zingo is another. This could vary library to library, function to function. It would work, and be sound design. I would just hate it is all :) Perhaps you have a better system in mind? -Steve
Apr 15 2016
parent reply Seb <seb wilzba.ch> writes:
On Friday, 15 April 2016 at 23:05:14 UTC, Steven Schveighoffer 
wrote:
 On 4/15/16 6:31 PM, Timon Gehr wrote:
 [...]
I'm sorry, should have put on my standard disclaimer that I am not a compiler writer :) I actually have no idea how this is done in the compiler, just how a compiler should behave as I understand it in my head. [...]
inout(bool) newsgroup.revive_discussion(); can we make the compiler smart enough to solve the use-cases of inout (=auto const/immutable deducation)?
May 01 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/1/16 7:58 PM, Seb wrote:
 On Friday, 15 April 2016 at 23:05:14 UTC, Steven Schveighoffer wrote:
 On 4/15/16 6:31 PM, Timon Gehr wrote:
 [...]
I'm sorry, should have put on my standard disclaimer that I am not a compiler writer :) I actually have no idea how this is done in the compiler, just how a compiler should behave as I understand it in my head. [...]
inout(bool) newsgroup.revive_discussion(); can we make the compiler smart enough to solve the use-cases of inout (=auto const/immutable deducation)?
If we can find a way to replace inout with something that does effectively everything inout does, then I'm all for it. -Steve
May 02 2016
prev sibling parent Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On 15/04/2016 04:10, Andrei Alexandrescu wrote:
 Commenting it out yields a number of unittest compilation errors,
 neither informative about the root of the problem and indicative as to
 how the parameter solves it.
...
 2. There needs to be documentation for people working on the standard
 library so they don't need to waste time on their own discovery process.
Perhaps use this, at least for now: // <Workaround explanation> alias InOutParam = inout int; ... (InOutParam) { ... } It's less cryptic at the use-site and people can lookup the definition to find an explanation.
Apr 16 2016