www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 9229] New: Private default arguments

reply d-bugmail puremagic.com writes:
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
next sibling parent d-bugmail puremagic.com writes:
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
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9229






 int 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
prev sibling next sibling parent d-bugmail puremagic.com writes:
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
prev sibling parent d-bugmail puremagic.com writes:
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