digitalmars.D.bugs - [Issue 9229] New: Private default arguments
- d-bugmail puremagic.com (120/120) Dec 27 2012 http://d.puremagic.com/issues/show_bug.cgi?id=9229
- d-bugmail puremagic.com (30/30) Dec 28 2012 http://d.puremagic.com/issues/show_bug.cgi?id=9229
- d-bugmail puremagic.com (14/20) Dec 28 2012 http://d.puremagic.com/issues/show_bug.cgi?id=9229
- d-bugmail puremagic.com (19/19) Dec 28 2012 http://d.puremagic.com/issues/show_bug.cgi?id=9229
- d-bugmail puremagic.com (7/7) Dec 30 2012 http://d.puremagic.com/issues/show_bug.cgi?id=9229
http://d.puremagic.com/issues/show_bug.cgi?id=9229 Summary: Private default arguments Product: D Version: D2 Platform: All OS/Version: All Status: NEW Severity: enhancement Priority: P2 Component: DMD AssignedTo: nobody puremagic.com ReportedBy: bearophile_hugs eml.cc This is a low priority enhancement request. It's a suggestion. Rather often recursive functions need one or more extra arguments that are not meant to be used by the caller: void radixSort(uint[] items, in uint shiftBits=24) { ... if (shiftBits > 0) { ... radixSort(array[...], shiftBits - 8); } } void main() { auto array = new uint[n]; ... array.radixSort(); } Here shiftBits is meant to be set meaningfully only by itself in the recursive calls (and to have a default value otherwise). In main() calling this radixSort function with a shiftBits value different from 24 is a bug. My experience shows similar "do not touch" default arguments are common enough for recursive functions. One way to avoid bugs and to not expose such private arguments is to define an inner function. Now the only argument of the outer function is 'items', and no mistakes can happen in using radixSort2(): void radixSort2(uint[] items) { void radix(in uint shiftBits=24) { ... if (shiftBits > 0) { ... radixSort(array[...], shiftBits - 8); } } radix(); } A nicer alternative is to introduce 'private' default arguments (they must have a default value): void radixSort3(uint[] items, private in uint shiftBits=24) { ... if (shiftBits > 0) { ... radixSort(array[...], shiftBits - 8); } } The 'private' means that only radixSort3 is allowed to set a shiftBits argument value. So in the main() function this is correct code: radixSort3(array); While this is a reported as compilation error: radixSort3(array, 26); A possible error message: Error: the 'shiftBits' argument of 'radixSort3' is private, it can be assigned only in inner recursive calls. Advantages: - This keeps the code shorter; - Avoids defining and naming the inner function; - Avoids one function call; - in the precedent example radix() was not a static function, this means such calls are a little slower. It's not hard to define a static radix() but lazy programmers sometimes forget that static annotation; - The code is more readable, there is only one function defined, and the "private" annotation denotes what's going on clearly; - The radixSort3 code is safe as with the inner function. Further notes: - Private arguments must have a default value. - Unlike private struct/class attributes, the privacy of such private function arguments is enforced even inside a module. - Class/struct constructors can't have private default arguments. - This requires no new keywords and this syntax clashes with no other syntax. - And regarding clashes against *future* syntaxes, I think the only other meaningful way to use a 'private' on arguments is for possible "case classes" as in Scala, but having D structs, I don't think this will happen. - Reargind how much easy it is to learn this new syntax, the "private" word is clear, but the meaning of private argument needs to be learnt, because it's different from the privacy of struct/class attributes. - This annotation causes no false positives/negatives. - The generated error message is probably enough for even new D programmers to understand what's going on. - Currently lambda functions can't call themselves, so private arguments aren't allowed in lambda functions. Once D gains a self() or __function or similar ways for lambdas to call themselves, then lambda private arguments should become allowed. - Mutually recursive functions are rare, so for simplicity private arguments are not allowed from a mutually recursive function. Only self calls are allowed to assign a private argument. - Pointers to functions with private default arguments act like regular default arguments, this means you have to specify all the arguments. - Variadic arguments can be private. - In ddoc private arguments can be shown in a different less visible color. They are present, but they are like a private API, so they are not so important for the normal user of the function. - In D I like the static safety offered by the compiler. This is another bit of static safety. - So far I don't remember serious bugs in my code caused by giving wrong "private" argument to recursive functions. But I practice defensive programming, and usually in such cases I define an inner function that takes the private argument to avoid bugs. - Currently recursive functions are not so common in D code. Maybe in future, if the functional style becomes more common, recursive functions will become more common. - Are private default arguments usable and useful for other use cases beside recursion? I don't know. Ideas welcome. (One idea is to allow only the other class/struct methods to set a private argument, but I think this is confusing, so I think this is a bad idea). - So this simple feature seems implementable. Is this feature worth the D language complexity increase it causes? -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 27 2012
http://d.puremagic.com/issues/show_bug.cgi?id=9229 Some more rules. The use of private arguments is allowed in pre/post conditions: int foo(private int n = 1) in { return foo(5); } body { return n; } If an argument is private, it needs to be private in its overriding/implementations too: interface IFoo { void foo(private int x = 5); } class Foo1 : IFoo { void foo(int x = 6) {} // Error. } class Bar1 { void foo(private int x = 7) {} void bar(int x = 8) {} } class Bar2: Bar1 { override void foo(int x = 7) {} // Error. override void bar(private int x = 8) {} // Error. } void main() {} -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 28 2012
http://d.puremagic.com/issues/show_bug.cgi?id=9229int foo(private int n = 1) in { return foo(5); } body { return n; }Sorry, I meant: int foo(private int n = 1) in { assert(foo(5)); // OK. } body { return n; } void main() {} -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 28 2012
http://d.puremagic.com/issues/show_bug.cgi?id=9229 One more trait is useful: void fooprivate(int x = 1, private int y = 1) { static assert(!__traits(isPrivate, x)); static assert(__traits(isPrivate, y)); } And std.traits.ParameterStorageClass needs "private_": alias ParameterStorageClass STC; // shorten the enum name void func(ref int ctx, out real result, real param, private int x = 1) { } alias ParameterStorageClassTuple!(func) pstc; static assert(pstc.length == 4); // three parameters static assert(pstc[0] == STC.ref_); static assert(pstc[1] == STC.out_); static assert(pstc[2] == STC.none); static assert(pstc[3] == STC.private_); -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 28 2012
http://d.puremagic.com/issues/show_bug.cgi?id=9229 See also the discussion thread: http://forum.dlang.org/thread/yfaammisrecggqzeiuem forum.dlang.org -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 30 2012