www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Tagging of arguments ref/out, or just out

reply bearophile <bearophileHUGS lycos.com> writes:
This is a recently opened (not by me) enhancement request thread:
http://d.puremagic.com/issues/show_bug.cgi?id=6442

It proposes something that I remember was discussed and refused two times in
past: to require (but only optionally!) "ref" and "out" at the calling point,


    void foo(ref int bar) { ... }
    int i = 0;
    foo(ref i);   // <------- here

    void foo(out int bar) { ... }
    int i = 0;
    foo(out i);   // <------- here


Jonathan M Davis has then argued that they clutter the code, and that making
them optional makes them kind of useless. See the thread for more details.

-----------------

After thinking some about it, I have suggested a related but alternative
proposal: to ask only for the "out" at the calling point, make it obligatory if
you compile with -warning and optional otherwise (for a long time "override"
was like this). I think having "out" at the calling point is more useful than
"ref".

Currently D 2.054 gives no warnings/errors on a program like this (I think the



void foo(out int x) {
    x = 5;
}
void main() {
    int y = 10;
    foo(y);
}


The problem here is the initialization of y to 10 always gets ignored.
Assigning something to y, *not using y in any way*, and then using it in a
"out" function argument call, is in my opinion a code smell. It's wasted code
at best, and sometimes it's related to possible semantic bugs.

Using "out" at the calling point doesn't fix that code, but helps the
programmer to see that the assign of 10 to y is useless, and it's better to
remove it:


void foo(out int x) {
    x = 5;
}
void main() {
    int y = 10;
    foo(out y);
}


In my opinion "ref" arguments don't have the same need of being tagged at the
calling point because a function that uses "ref" often reads and writes the
argument (otherwise you use "in" or "out"), so in a ref argument assigning
something to y before the call is more often meaningful:


void foo(ref int x) {
    x++;
}
int main() {
    int y = 10;
    return foo(y);
}

Bye,
bearophile
Aug 07 2011
next sibling parent reply Max Klyga <max.klyga gmail.com> writes:

versioning. If some function took an argument by value but was changed 
to take if by reference, without annotations compiler would treat this 
as an error, preventing a potential bug.

So this feature has to be either mandatory or not. Making it optional 
leads to confusion as Jonathan mentioned.
But it's kinda late to change the language now, with all the code outthere.

On 2011-08-07 23:42:30 +0300, bearophile said:

 This is a recently opened (not by me) enhancement request thread:
 http://d.puremagic.com/issues/show_bug.cgi?id=6442
 
 It proposes something that I remember was discussed and refused two 
 times in past: to require (but only optionally!) "ref" and "out" at the 

 
     void foo(ref int bar) { ... }
     int i = 0;
     foo(ref i);   // <------- here
 
     void foo(out int bar) { ... }
     int i = 0;
     foo(out i);   // <------- here
 
 
 Jonathan M Davis has then argued that they clutter the code, and that 
 making them optional makes them kind of useless. See the thread for 
 more details.
 
 -----------------
 
 After thinking some about it, I have suggested a related but 
 alternative proposal: to ask only for the "out" at the calling point, 
 make it obligatory if you compile with -warning and optional otherwise 
 (for a long time "override" was like this). I think having "out" at the 
 calling point is more useful than "ref".
 
 Currently D 2.054 gives no warnings/errors on a program like this (I 

 
 
 void foo(out int x) {
     x = 5;
 }
 void main() {
     int y = 10;
     foo(y);
 }
 
 
 The problem here is the initialization of y to 10 always gets ignored. 
 Assigning something to y, *not using y in any way*, and then using it 
 in a "out" function argument call, is in my opinion a code smell. It's 
 wasted code at best, and sometimes it's related to possible semantic 
 bugs.
 
 Using "out" at the calling point doesn't fix that code, but helps the 
 programmer to see that the assign of 10 to y is useless, and it's 
 better to remove it:
 
 
 void foo(out int x) {
     x = 5;
 }
 void main() {
     int y = 10;
     foo(out y);
 }
 
 
 In my opinion "ref" arguments don't have the same need of being tagged 
 at the calling point because a function that uses "ref" often reads and 
 writes the argument (otherwise you use "in" or "out"), so in a ref 
 argument assigning something to y before the call is more often 
 meaningful:
 
 
 void foo(ref int x) {
     x++;
 }
 int main() {
     int y = 10;
     return foo(y);
 }
 
 Bye,
 bearophile
Aug 07 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
Max Klyga:
 
 So this feature has to be either mandatory or not. Making it optional 
 leads to confusion as Jonathan mentioned.
On this, what I have suggested it not the same thing that the original proposal says. In the original proposal the "ref" and "out" are optional. In my alternative proposal if you compile with "-w" you are sure that the compiler will ask you "out" at the calling point too.
 But it's kinda late to change the language now, with all the code outthere.
It will take some more years to have enough D2 code out there to make a breaking but incremental change like this too much costly for people. (In Bugzilla I have few more little breaking changes that are more important than this "out" thing. And other things will need to be fixed, in "const" etc. Several of them will break D code in ways more costly than requiring to add a "out" in code here and there). Bye, bearophile
Aug 07 2011
parent Don <nospam nospam.com> writes:
bearophile wrote:
 Max Klyga:
  
 So this feature has to be either mandatory or not. Making it optional 
 leads to confusion as Jonathan mentioned.
On this, what I have suggested it not the same thing that the original proposal says. In the original proposal the "ref" and "out" are optional. In my alternative proposal if you compile with "-w" you are sure that the compiler will ask you "out" at the calling point too.
Please bury forever the idea that "mandatory when compiled with -w" has any practical difference from "mandatory".
Aug 08 2011
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-08-07 20:42:30 +0000, bearophile <bearophileHUGS lycos.com> said:

 This is a recently opened (not by me) enhancement request thread:
 http://d.puremagic.com/issues/show_bug.cgi?id=6442
 
 It proposes something that I remember was discussed and refused two 
 times in past: to require (but only optionally!) "ref" and "out" at the 

 
     void foo(ref int bar) { ... }
     int i = 0;
     foo(ref i);   // <------- here
 
     void foo(out int bar) { ... }
     int i = 0;
     foo(out i);   // <------- here
 
 
 Jonathan M Davis has then argued that they clutter the code, and that 
 making them optional makes them kind of useless. See the thread for 
 more details.
 
 -----------------
 
 After thinking some about it, I have suggested a related but 
 alternative proposal: to ask only for the "out" at the calling point, 
 make it obligatory if you compile with -warning and optional otherwise 
 (for a long time "override" was like this). I think having "out" at the 
 calling point is more useful than "ref".
 
 […]
I don't find your arguments very enticing, sorry. But I do see *one* solid reason we might want to add 'ref' and 'out', optional or not, at the call site: variadic templates. I recently had to use getopt. You use it like that: bool option; int counter; getopt(args, "option|o", &option, "counter|c", &counter); The problem is that taking addresses of a stack variable is disabled in SafeD, which means getopt doesn't work in SafeD, which puts SafeD in a strange position. It'd work if we used 'ref' parameters. The problem is that getopt is defined like this: void getopt(T...)(ref string[] args, T opts); Using a type tuple makes it impossible to have 'ref's where they need to be, hence the use of pointers. Type tuples could be made support propagating the type including the 'ref' or 'out' storage class applied on them, which would allow you to write this: getopt!(string, ref bool, string, ref int)(args, "option|o", option, "counter|c", counter); But that doesn't work with type deduction. It could work however if you allowed specifying the 'ref' as part of the argument list: getopt(args, "option|o", ref option, "counter|c", ref counter); I'd prefer if we had a solution that doesn't require you to write 'ref' at the call site, but I haven't found any. Ideas? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 08 2011
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
Michel Fortin wrote:
 On 2011-08-07 20:42:30 +0000, bearophile <bearophileHUGS lycos.com> said:

 This is a recently opened (not by me) enhancement request thread:
 http://d.puremagic.com/issues/show_bug.cgi?id=6442

 It proposes something that I remember was discussed and refused two
 times in past: to require (but only optionally!) "ref" and "out" at the


     void foo(ref int bar) { ... }
     int i = 0;
     foo(ref i);   // <------- here

     void foo(out int bar) { ... }
     int i = 0;
     foo(out i);   // <------- here


 Jonathan M Davis has then argued that they clutter the code, and that
 making them optional makes them kind of useless. See the thread for
 more details.

 -----------------

 After thinking some about it, I have suggested a related but
 alternative proposal: to ask only for the "out" at the calling point,
 make it obligatory if you compile with -warning and optional otherwise
 (for a long time "override" was like this). I think having "out" at the
 calling point is more useful than "ref".

 […]
I don't find your arguments very enticing, sorry. But I do see *one* solid reason we might want to add 'ref' and 'out', optional or not, at the call site: variadic templates. I recently had to use getopt. You use it like that: bool option; int counter; getopt(args, "option|o", &option, "counter|c", &counter); The problem is that taking addresses of a stack variable is disabled in SafeD, which means getopt doesn't work in SafeD, which puts SafeD in a strange position. It'd work if we used 'ref' parameters. The problem is that getopt is defined like this: void getopt(T...)(ref string[] args, T opts); Using a type tuple makes it impossible to have 'ref's where they need to be, hence the use of pointers. Type tuples could be made support propagating the type including the 'ref' or 'out' storage class applied on them, which would allow you to write this: getopt!(string, ref bool, string, ref int)(args, "option|o", option, "counter|c", counter); But that doesn't work with type deduction. It could work however if you allowed specifying the 'ref' as part of the argument list: getopt(args, "option|o", ref option, "counter|c", ref counter); I'd prefer if we had a solution that doesn't require you to write 'ref' at the call site, but I haven't found any. Ideas? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
void getopt(T...)(ref string[] args, ref T opts); Would work afaik (but all the flag names would be passed by ref too). std.stdio.readf has the same issue. The general problem is that type deduction does not currently work too well with implicit conversions (that is also the reason why most string algorithms in Phobos don't work with immutable strings) I can see a possible solution to the problem: At definition side, it should be possible to manually influence type and storage class deduction. But I cannot think of syntax for this that wouldn't look awkward. Any ideas?
Aug 08 2011
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
Michel Fortin Wrote:

 I recently had to use getopt. You use it like that:
 
 	bool option;
 	int counter;
 
 	getopt(args,
 		"option|o", &option,
 		"counter|c", &counter);
 
 The problem is that taking addresses of a stack variable is disabled in 
 SafeD, which means getopt doesn't work in SafeD, which puts SafeD in a 
 strange position.
const opts = getopt(args); const option = opts.get!bool("option|o"); const counter = opts.get!int("counter|c"); // also reusable
Aug 08 2011
next sibling parent Kagamin <spam here.lot> writes:
Though I think, `main` arguments should eliminated altogether and be accessed
statically.

const ReusableOpts opts = getopts();

const string[] args = mainArgs; //  property: parse args on demand, remove the
code from startup.
Aug 08 2011
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2011-08-08 18:30:10 +0000, Kagamin <spam here.lot> said:

 Michel Fortin Wrote:
 
 I recently had to use getopt. You use it like that:
 
 	bool option;
 	int counter;
 
 	getopt(args,
 		"option|o", &option,
 		"counter|c", &counter);
 
 The problem is that taking addresses of a stack variable is disabled in
 SafeD, which means getopt doesn't work in SafeD, which puts SafeD in a
 strange position.
const opts = getopt(args); const option = opts.get!bool("option|o"); const counter = opts.get!int("counter|c"); // also reusable
Thanks for the tip. That said it doesn't invalidate my complain about 'ref' and 'out' being unusable in conjunction with type tuple arguments. If you're going to write a function that forwards its parameters to another function, using a type tuple to make it generic will fail in the presence of 'ref' and 'out' parameters in the wrapped function. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 08 2011
prev sibling parent kenji hara <k.hara.pg gmail.com> writes:
I think we can use "auto ref".
2011/08/08 21:45 "Michel Fortin" <michel.fortin michelf.com>:
 On 2011-08-07 20:42:30 +0000, bearophile <bearophileHUGS lycos.com> said:

 This is a recently opened (not by me) enhancement request thread:
 http://d.puremagic.com/issues/show_bug.cgi?id=3D6442

 It proposes something that I remember was discussed and refused two
 times in past: to require (but only optionally!) "ref" and "out" at the


 void foo(ref int bar) { ... }
 int i =3D 0;
 foo(ref i); // <------- here

 void foo(out int bar) { ... }
 int i =3D 0;
 foo(out i); // <------- here


 Jonathan M Davis has then argued that they clutter the code, and that
 making them optional makes them kind of useless. See the thread for
 more details.

 -----------------

 After thinking some about it, I have suggested a related but
 alternative proposal: to ask only for the "out" at the calling point,
 make it obligatory if you compile with -warning and optional otherwise
 (for a long time "override" was like this). I think having "out" at the
 calling point is more useful than "ref".

 [=85]
I don't find your arguments very enticing, sorry. But I do see *one* solid reason we might want to add 'ref' and 'out', optional or not, at the call site: variadic templates. I recently had to use getopt. You use it like that: bool option; int counter; getopt(args, "option|o", &option, "counter|c", &counter); The problem is that taking addresses of a stack variable is disabled in SafeD, which means getopt doesn't work in SafeD, which puts SafeD in a strange position. It'd work if we used 'ref' parameters. The problem is that getopt is defined like this: void getopt(T...)(ref string[] args, T opts); Using a type tuple makes it impossible to have 'ref's where they need to be, hence the use of pointers. Type tuples could be made support propagating the type including the 'ref' or 'out' storage class applied on them, which would allow you to write this: getopt!(string, ref bool, string, ref int)(args, "option|o", option, "counter|c", counter); But that doesn't work with type deduction. It could work however if you allowed specifying the 'ref' as part of the argument list: getopt(args, "option|o", ref option, "counter|c", ref counter); I'd prefer if we had a solution that doesn't require you to write 'ref' at the call site, but I haven't found any. Ideas? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 08 2011
prev sibling parent reply Kagamin <spam here.lot> writes:
bearophile Wrote:

 This is a recently opened (not by me) enhancement request thread:
 http://d.puremagic.com/issues/show_bug.cgi?id=6442
D is the language to save keystrokes. This proposal is plain invalid.
Aug 08 2011
next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
On Aug 9, 11 02:32, Kagamin wrote:
 bearophile Wrote:

 This is a recently opened (not by me) enhancement request thread:
 http://d.puremagic.com/issues/show_bug.cgi?id=6442
D is the language to save keystrokes. This proposal is plain invalid.
I disagree. If you want to save keystrokes, use Perl. "D is a multi-paradigm programming language that combines a principled approach with a focus on practicality. In D you get to harness the power and high performance of C and C++ and also the safety and programmer productivity of modern languages such as Ruby and Python. Special attention is given to the needs of quality assurance, documentation, portability, and reliability." Nowhere mentions saving keystrokes. The proposal is OK, as a mean to provide reliability (where you ensure a variable passed is not modified in opaque because it is pass by 'ref') and documentation at call-site.
Aug 08 2011
parent reply Kagamin <spam here.lot> writes:
KennyTM~ Wrote:

 I disagree. If you want to save keystrokes, use Perl.
Perl is dynamically typed, right? D is statically typed, so it can statically check most things like out variables won't overwrite const arguments.
Aug 08 2011
parent reply Ziad Hatahet <hatahet gmail.com> writes:
FWIW, Google's C++ style guide explicitly requires passing pointers to
arguments (whenever possible) when they are to be modified or used as out
parameters, and passed by const& when they are not. This makes it more
obvious at the caller's end which parameters are going to be modified and
which ones aren't.

--
Ziad


On Mon, Aug 8, 2011 at 12:23 PM, Kagamin <spam here.lot> wrote:

 KennyTM~ Wrote:

 I disagree. If you want to save keystrokes, use Perl.
Perl is dynamically typed, right? D is statically typed, so it can statically check most things like out variables won't overwrite const arguments.
Aug 08 2011
parent bearophile <bearophileHUGS lycos.com> writes:
Ziad Hatahet:

 FWIW, Google's C++ style guide explicitly requires passing pointers to
 arguments (whenever possible) when they are to be modified or used as out
 parameters, and passed by const& when they are not.
It's here: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments Bye, bearophile
Aug 08 2011
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 bearophile Wrote:
 This is a recently opened (not by me) enhancement request thread:
 http://d.puremagic.com/issues/show_bug.cgi?id=6442
D is the language to save keystrokes. This proposal is plain invalid.
We're not going to reject a feature proposal simply because it involves more typing (unless it's excessive). What matters are what overall advantages and disadvantages it provides. Saving keystrokes is nice, but it's generally not enough of a reason to accept or reject a feature proposal. D does generally let you do more in less code than C++, but saving keystrokes is just a nice bonus on top of everything else that it provides, not the reason for its existance. - Jonathan M Davis
Aug 08 2011