Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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)?
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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)
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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)
Re: So what does (inout int = 0) do?
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()" }
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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 : 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?
Re: So what does (inout int = 0) do?
Am Fri, 15 Apr 2016 09:44:05 -0400 schrieb Andrei Alexandrescu : inout must go. -- Andrei Ceterum censeo Carthaginem esse delendam. -- Marcus Porcius Cato :o) -- Marco
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
(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[]`.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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: // alias InOutParam = inout int; ... (InOutParam) { ... } It's less cryptic at the use-site and people can lookup the definition to find an explanation.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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 logica
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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) {} ... }
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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?
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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!
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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.
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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).
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
Re: So what does (inout int = 0) do?
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
So what does (inout int = 0) do?
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