www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Hidden argument kind antipattern

reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
To elaborate, I mean allowing code which appears to behave surprisingly  
different from the at-a-glance interpretation, unless the programmer knows  
the function's signature. I've noticed a worrying adoption in D of this  
"antipattern", which, frankly, I believe doesn't belong in a well-designed  
programming language. One classic example of this is passing arguments by  
reference, something D inherited from C++. For example:

int x, y;
// ...
someObject.updateCoords(x, y);

What can you say about this code? The unobvious and surprising  
interpretation of it is that updateCoords will change the values of x and  
y. C# solved this problem neatly by requiring to specify the "ref" keyword  
before the function arguments:

someObject.updateCoords(ref x, ref y); // much clearer

This problem carries over to lazy parameters, as well. I'll quote a line  
of code from a recent post by David Simcha:

 auto connections = taskPool.workerLocalStorage(new MysqlConnection());

Anyone who is not familiar with std.parallelism and D's lazy parameter feature is going to be very surprised to find out what this code really does. This might be OK for us, experienced D users, but think about the poor bloke who will someday, somewhere try to debug a D program written by someone else, and tear his hair out trying to figure out why an expression passed as an argument to some function isn't being evaluated before/during the function call. (The solution I would suggest is simply having to specify the "lazy" keyword before the expression of each lazy parameter, same as C#'s "ref". This will require updating all usage of "enforce" among other changes.) I know that I should have brought this up during the module's review phase, so take the following with a grain of salt: in my opinion, std.parallelism's usage of lazy parameters overreaches their intended use and borders into abuse of this language feature. For this reason (and meaning no disrespect towards David and everyone who participated in shaping the module), I am uncomfortable with the inclusion of std.parallelism into the standard D library, as it would carry the message that D's maintainers encourage abusing the language in such ways. (This probably doesn't count as a vote.) For the moment, I would suggest changing all lazy parameters which are evaluated in different contexts (outside of the said function) into delegates, and replacing their usage with delegate literals. -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Apr 19 2011
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Vladimir Panteleev (vladimir thecybershadow.net)'s article
 To elaborate, I mean allowing code which appears to behave surprisingly
 different from the at-a-glance interpretation, unless the programmer knows
 the function's signature. I've noticed a worrying adoption in D of this
 "antipattern", which, frankly, I believe doesn't belong in a well-designed
 programming language. One classic example of this is passing arguments by
 reference, something D inherited from C++. For example:
 int x, y;
 // ...
 someObject.updateCoords(x, y);
 What can you say about this code? The unobvious and surprising
 interpretation of it is that updateCoords will change the values of x and
 y. C# solved this problem neatly by requiring to specify the "ref" keyword
 before the function arguments:
 someObject.updateCoords(ref x, ref y); // much clearer
 This problem carries over to lazy parameters, as well. I'll quote a line
 of code from a recent post by David Simcha:
 auto connections = taskPool.workerLocalStorage(new MysqlConnection());

feature is going to be very surprised to find out what this code really does. This might be OK for us, experienced D users, but think about the poor bloke who will someday, somewhere try to debug a D program written by someone else, and tear his hair out trying to figure out why an expression passed as an argument to some function isn't being evaluated before/during the function call. (The solution I would suggest is simply having to specify the "lazy" keyword before the expression of each lazy parameter, same as C#'s "ref". This will require updating all usage of "enforce" among other changes.) I know that I should have brought this up during the module's review phase, so take the following with a grain of salt: in my opinion, std.parallelism's usage of lazy parameters overreaches their intended use and borders into abuse of this language feature. For this reason (and meaning no disrespect towards David and everyone who participated in shaping the module), I am uncomfortable with the inclusion of std.parallelism into the standard D library, as it would carry the message that D's maintainers encourage abusing the language in such ways. (This probably doesn't count as a vote.) For the moment, I would suggest changing all lazy parameters which are evaluated in different contexts (outside of the said function) into delegates, and replacing their usage with delegate literals.

Interesting point. I was questioning whether this was really a good idea myself. I did it because it seemed like things like this were kind of the point of lazy parameters. Let's have a good discussion on this now, and if we decide that my idiom is an abuse of lazy parameters, then I'll switch it over to a delegate literal. My vote is weakly in favor of lazy parameters, since a maintenance programmer would probably look at what such a function does and not be surprised for very long. However, I'm not dead-set against changing it if there's a strong consensus that it should be changed.
Apr 19 2011
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
dsimcha:

  I did it because it seemed like things like this were kind of the point of
lazy
 parameters.

I agree. In my opinion the problem is not you using in your module a language feature designed for that very purpose. What's in question is the syntax of that language feature. I don't mind lazy arguments, but it can be argued that's better for them to require a "lazy" prefix at the calling point too. This little syntax problem that was already raised during the D2 design phase. Two little disadvantages of requiring "lazy" prefix at the calling point too are: - A little more code to write. This is not really a typing problem of five extra lowercase chars (there is a space too), but a problem of succinctness and syntax noise. I think being it lower case the added syntax noise is not so high. - It's less general code. Changing a normal argument from lazy to not lazy or from not lazy to lazy becomes not transparent any more, it requires changes at all call points. Recent versions of C# have softened the requirements of the "ref" annotation at the calling point, when using COM: http://en.wikipedia.org/wiki/C_Sharp_4.0#Optional_ref_Keyword_when_using_COM I am undecided still. I weakly agree to require "lazy" suffix at the calling point too. Bye, bearophile
Apr 19 2011
parent dsimcha <dsimcha yahoo.com> writes:
On 4/19/2011 7:47 PM, Vladimir Panteleev wrote:
 David's library evaluates these expressions *in different threads*!

Minor correction: No it doesn't. It constructs a WorkerLocalStorage object in a regular class method in the thread from which workerLocalStorage() was called. It does evaluate the lazy parameter more than once, but all the multithreading stuff happens **after** workerLocalStorage() returns. I still see your point, though.
Apr 19 2011
prev sibling next sibling parent Kai Meyer <kai unixlords.com> writes:
On 04/19/2011 05:18 PM, dsimcha wrote:
 == Quote from Vladimir Panteleev (vladimir thecybershadow.net)'s article
 To elaborate, I mean allowing code which appears to behave surprisingly
 different from the at-a-glance interpretation, unless the programmer knows
 the function's signature. I've noticed a worrying adoption in D of this
 "antipattern", which, frankly, I believe doesn't belong in a well-designed
 programming language. One classic example of this is passing arguments by
 reference, something D inherited from C++. For example:
 int x, y;
 // ...
 someObject.updateCoords(x, y);
 What can you say about this code? The unobvious and surprising
 interpretation of it is that updateCoords will change the values of x and
 y. C# solved this problem neatly by requiring to specify the "ref" keyword
 before the function arguments:
 someObject.updateCoords(ref x, ref y); // much clearer
 This problem carries over to lazy parameters, as well. I'll quote a line
 of code from a recent post by David Simcha:
 auto connections = taskPool.workerLocalStorage(new MysqlConnection());

feature is going to be very surprised to find out what this code really does. This might be OK for us, experienced D users, but think about the poor bloke who will someday, somewhere try to debug a D program written by someone else, and tear his hair out trying to figure out why an expression passed as an argument to some function isn't being evaluated before/during the function call. (The solution I would suggest is simply having to specify the "lazy" keyword before the expression of each lazy parameter, same as C#'s "ref". This will require updating all usage of "enforce" among other changes.) I know that I should have brought this up during the module's review phase, so take the following with a grain of salt: in my opinion, std.parallelism's usage of lazy parameters overreaches their intended use and borders into abuse of this language feature. For this reason (and meaning no disrespect towards David and everyone who participated in shaping the module), I am uncomfortable with the inclusion of std.parallelism into the standard D library, as it would carry the message that D's maintainers encourage abusing the language in such ways. (This probably doesn't count as a vote.) For the moment, I would suggest changing all lazy parameters which are evaluated in different contexts (outside of the said function) into delegates, and replacing their usage with delegate literals.

Interesting point. I was questioning whether this was really a good idea myself. I did it because it seemed like things like this were kind of the point of lazy parameters. Let's have a good discussion on this now, and if we decide that my idiom is an abuse of lazy parameters, then I'll switch it over to a delegate literal. My vote is weakly in favor of lazy parameters, since a maintenance programmer would probably look at what such a function does and not be surprised for very long. However, I'm not dead-set against changing it if there's a strong consensus that it should be changed.

I would agree with the "ref" and "lazy" modifiers to parameters. If they exist in the function signature, it seems potentially confusing to "silently" accept a built-in or other stack-based parameter as a reference with out an explicit "ref" in the call. For instance: import std.stdio; struct Bar { public int a; } class Foo { public int a; } void main() { int a; Bar b = Bar(); Foo f = new Foo(); a = 1; b.a = 1; f.a = 1; writef("a=%d b.a=%d f.a=%d\n", a, b.a, f.a); changeit(a); changeit(b); changeit(f); writef("a=%d b.a=%d f.a=%d\n", a, b.a, f.a); changeit_ref(a); changeit_ref(b); changeit_ref(f); writef("a=%d b.a=%d f.a=%d\n", a, b.a, f.a); } void changeit(int baz) { baz = 2; } void changeit(Bar bar) { bar.a = 2; } void changeit(Foo foo) { foo.a = 2; } void changeit_ref(ref int baz) { baz = 3; } void changeit_ref(ref Bar bar) { bar.a = 3; } void changeit_ref(Foo foo) { foo.a = 3; } //Output: //a=1 b.a=1 f.a=1 //a=1 b.a=1 f.a=2 //a=3 b.a=3 f.a=3 The call signatures for changeit and changeit_ref are exactly the same, but the function definition signatures are different. In the example above, I can see the value in forcing the caller to use "changeit_ref(ref a)" to match the function declaration.
Apr 19 2011
prev sibling next sibling parent dsimcha <dsimcha yahoo.com> writes:
On 4/19/2011 7:30 PM, spir wrote:
 And what about requiring "lazy" (and "ref") at the call site; I mean in
 the long term? I find this very sensible.

 Denis

I wouldn't mind this for "lazy" but would argue heavily against it for "ref". ref parameters are both more frequently used and less surprising than lazy parameters and usually the name and usage of the function give a stronger hint about what it does. Secondly, functions may modify their input parameters in surprising ways without ref, if the parameters contain indirection (for example, classes, pointers, arrays). Furthermore, requiring ref at the call site would break uniform function call syntax. For example: void popFront(T)(ref T[] array) { array = array[1..$]; } void main() { auto foo = [1, 2, 3]; foo.popFront(); }
Apr 19 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 04/20/2011 01:42 AM, Kai Meyer wrote:
 On 04/19/2011 05:18 PM, dsimcha wrote:
 == Quote from Vladimir Panteleev (vladimir thecybershadow.net)'s article
 To elaborate, I mean allowing code which appears to behave surprisingly
 different from the at-a-glance interpretation, unless the programmer knows
 the function's signature. I've noticed a worrying adoption in D of this
 "antipattern", which, frankly, I believe doesn't belong in a well-designed
 programming language. One classic example of this is passing arguments by
 reference, something D inherited from C++. For example:
 int x, y;
 // ...
 someObject.updateCoords(x, y);
 What can you say about this code? The unobvious and surprising
 interpretation of it is that updateCoords will change the values of x and
 y. C# solved this problem neatly by requiring to specify the "ref" keyword
 before the function arguments:
 someObject.updateCoords(ref x, ref y); // much clearer
 This problem carries over to lazy parameters, as well. I'll quote a line
 of code from a recent post by David Simcha:
 auto connections = taskPool.workerLocalStorage(new MysqlConnection());

feature is going to be very surprised to find out what this code really does. This might be OK for us, experienced D users, but think about the poor bloke who will someday, somewhere try to debug a D program written by someone else, and tear his hair out trying to figure out why an expression passed as an argument to some function isn't being evaluated before/during the function call. (The solution I would suggest is simply having to specify the "lazy" keyword before the expression of each lazy parameter, same as C#'s "ref". This will require updating all usage of "enforce" among other changes.) I know that I should have brought this up during the module's review phase, so take the following with a grain of salt: in my opinion, std.parallelism's usage of lazy parameters overreaches their intended use and borders into abuse of this language feature. For this reason (and meaning no disrespect towards David and everyone who participated in shaping the module), I am uncomfortable with the inclusion of std.parallelism into the standard D library, as it would carry the message that D's maintainers encourage abusing the language in such ways. (This probably doesn't count as a vote.) For the moment, I would suggest changing all lazy parameters which are evaluated in different contexts (outside of the said function) into delegates, and replacing their usage with delegate literals.

Interesting point. I was questioning whether this was really a good idea myself. I did it because it seemed like things like this were kind of the point of lazy parameters. Let's have a good discussion on this now, and if we decide that my idiom is an abuse of lazy parameters, then I'll switch it over to a delegate literal. My vote is weakly in favor of lazy parameters, since a maintenance programmer would probably look at what such a function does and not be surprised for very long. However, I'm not dead-set against changing it if there's a strong consensus that it should be changed.

I would agree with the "ref" and "lazy" modifiers to parameters. If they exist in the function signature, it seems potentially confusing to "silently" accept a built-in or other stack-based parameter as a reference with out an explicit "ref" in the call. For instance: import std.stdio; struct Bar { public int a; } class Foo { public int a; } void main() { int a; Bar b = Bar(); Foo f = new Foo(); a = 1; b.a = 1; f.a = 1; writef("a=%d b.a=%d f.a=%d\n", a, b.a, f.a); changeit(a); changeit(b); changeit(f); writef("a=%d b.a=%d f.a=%d\n", a, b.a, f.a); changeit_ref(a); changeit_ref(b); changeit_ref(f); writef("a=%d b.a=%d f.a=%d\n", a, b.a, f.a); } void changeit(int baz) { baz = 2; } void changeit(Bar bar) { bar.a = 2; } void changeit(Foo foo) { foo.a = 2; } void changeit_ref(ref int baz) { baz = 3; } void changeit_ref(ref Bar bar) { bar.a = 3; } void changeit_ref(Foo foo) { foo.a = 3; } //Output: //a=1 b.a=1 f.a=1 //a=1 b.a=1 f.a=2 //a=3 b.a=3 f.a=3 The call signatures for changeit and changeit_ref are exactly the same, but the function definition signatures are different. In the example above, I can see the value in forcing the caller to use "changeit_ref(ref a)" to match the function declaration.

That's exactly my point of view. It looks stupid & redondant at first sight, but its value lies in understanding what code *actually* does, and how. Another example, from real code: private static Code[] codesFromUTF8 (string s) { Code[] codes; uint iChar = 0; // must be uint for UTFDecode while (iChar < s.length) { // Note: iChar is taken by ref and advanced codes ~= std.utf.decode(s, iChar); } return codes; } I guess without the note it's nearly imossible to understand how this may well work. Magic? Denis -- _________________ vita es estrany spir.wikidot.com
Apr 20 2011
prev sibling parent spir <denis.spir gmail.com> writes:
On 04/20/2011 01:53 AM, dsimcha wrote:
 On 4/19/2011 7:30 PM, spir wrote:
 And what about requiring "lazy" (and "ref") at the call site; I mean in
 the long term? I find this very sensible.

 Denis

I wouldn't mind this for "lazy" but would argue heavily against it for "ref". ref parameters are both more frequently used and less surprising than lazy parameters and usually the name and usage of the function give a stronger hint about what it does. Secondly, functions may modify their input parameters in surprising ways without ref, if the parameters contain indirection (for example, classes, pointers, arrays). Furthermore, requiring ref at the call site would break uniform function call syntax. For example: void popFront(T)(ref T[] array) { array = array[1..$]; } void main() { auto foo = [1, 2, 3]; foo.popFront(); }

I rather agree in this case; this is due to correct naming of the called function. See the example of calling std.utf.decode in my reply to Kai Meyer. Another example: void first(T)(ref T[] array) { auto elem = array[0]; array = array[1..$]; return elem; } void main() { auto foo = [1, 2, 3]; auto elem = first(foo); // or even: while (foo.length > 0) writeln(first(foo)); } I think Vladimir's point of the actual task performed by a function beeing "surprising" is valid. Now, it's true this would break UFCS, here foo.first(). But note that this is OO-like call syntax, and commonly in OO the receiver is passed by ref (precisely for beeing changeable, I guess); the object on which a method applies is not a (normal) parameter. Also, the more UFCS gets discussed, the more is raises questions, for debattable advantage. Denis -- _________________ vita es estrany spir.wikidot.com
Apr 20 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 04/20/2011 01:09 AM, Vladimir Panteleev wrote:
 To elaborate, I mean allowing code which appears to behave surprisingly
 different from the at-a-glance interpretation, unless the programmer knows the
 function's signature. I've noticed a worrying adoption in D of this
 "antipattern", which, frankly, I believe doesn't belong in a well-designed
 programming language. One classic example of this is passing arguments by
 reference, something D inherited from C++. For example:

 int x, y;
 // ...
 someObject.updateCoords(x, y);

 What can you say about this code? The unobvious and surprising interpretation
 of it is that updateCoords will change the values of x and y. C# solved this
 problem neatly by requiring to specify the "ref" keyword before the function
 arguments:

 someObject.updateCoords(ref x, ref y); // much clearer

 This problem carries over to lazy parameters, as well. I'll quote a line of
 code from a recent post by David Simcha:

 auto connections = taskPool.workerLocalStorage(new MysqlConnection());

Anyone who is not familiar with std.parallelism and D's lazy parameter feature is going to be very surprised to find out what this code really does. This might be OK for us, experienced D users, but think about the poor bloke who will someday, somewhere try to debug a D program written by someone else, and tear his hair out trying to figure out why an expression passed as an argument to some function isn't being evaluated before/during the function call. (The solution I would suggest is simply having to specify the "lazy" keyword before the expression of each lazy parameter, same as C#'s "ref". This will require updating all usage of "enforce" among other changes.) [...current vote about std.parallel...] For the moment, I would suggest changing all lazy parameters which are evaluated in different contexts (outside of the said function) into delegates, and replacing their usage with delegate literals.

Sounds very good to me. Denis -- _________________ vita es estrany spir.wikidot.com
Apr 19 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 04/20/2011 01:18 AM, dsimcha wrote:
 == Quote from Vladimir Panteleev (vladimir thecybershadow.net)'s article
 To elaborate, I mean allowing code which appears to behave surprisingly
 different from the at-a-glance interpretation, unless the programmer knows
 the function's signature. I've noticed a worrying adoption in D of this
 "antipattern", which, frankly, I believe doesn't belong in a well-designed
 programming language. One classic example of this is passing arguments by
 reference, something D inherited from C++. For example:
 int x, y;
 // ...
 someObject.updateCoords(x, y);
 What can you say about this code? The unobvious and surprising
 interpretation of it is that updateCoords will change the values of x and
 y. C# solved this problem neatly by requiring to specify the "ref" keyword
 before the function arguments:
 someObject.updateCoords(ref x, ref y); // much clearer
 This problem carries over to lazy parameters, as well. I'll quote a line
 of code from a recent post by David Simcha:
 auto connections = taskPool.workerLocalStorage(new MysqlConnection());

feature is going to be very surprised to find out what this code really does. This might be OK for us, experienced D users, but think about the poor bloke who will someday, somewhere try to debug a D program written by someone else, and tear his hair out trying to figure out why an expression passed as an argument to some function isn't being evaluated before/during the function call. (The solution I would suggest is simply having to specify the "lazy" keyword before the expression of each lazy parameter, same as C#'s "ref". This will require updating all usage of "enforce" among other changes.) I know that I should have brought this up during the module's review phase, so take the following with a grain of salt: in my opinion, std.parallelism's usage of lazy parameters overreaches their intended use and borders into abuse of this language feature. For this reason (and meaning no disrespect towards David and everyone who participated in shaping the module), I am uncomfortable with the inclusion of std.parallelism into the standard D library, as it would carry the message that D's maintainers encourage abusing the language in such ways. (This probably doesn't count as a vote.) For the moment, I would suggest changing all lazy parameters which are evaluated in different contexts (outside of the said function) into delegates, and replacing their usage with delegate literals.

Interesting point. I was questioning whether this was really a good idea myself. I did it because it seemed like things like this were kind of the point of lazy parameters. Let's have a good discussion on this now, and if we decide that my idiom is an abuse of lazy parameters, then I'll switch it over to a delegate literal. My vote is weakly in favor of lazy parameters, since a maintenance programmer would probably look at what such a function does and not be surprised for very long. However, I'm not dead-set against changing it if there's a strong consensus that it should be changed.

And what about requiring "lazy" (and "ref") at the call site; I mean in the long term? I find this very sensible. Denis -- _________________ vita es estrany spir.wikidot.com
Apr 19 2011
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Wed, 20 Apr 2011 02:18:39 +0300, dsimcha <dsimcha yahoo.com> wrote:

 My vote is weakly in favor of lazy parameters, since a maintenance  
 programmer would probably look at what such a function does and not be  
 surprised for very long.

Not contradicting you, but I'd like to clarify my point: given a large function with unknown code, it is my opinion that, with a well-designed programming language / library, a maintainer will not have to inspect the signature of each function referenced from the code to understand the basic _data flow_ and _execution order_ within the function. -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Apr 19 2011
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Wed, 20 Apr 2011 02:43:50 +0300, bearophile <bearophileHUGS lycos.com>  
wrote:

 I agree. In my opinion the problem is not you using in your module a  
 language feature designed for that very purpose. What's in question is  
 the syntax of that language feature. I don't mind lazy arguments, but it  
 can be argued that's better for them to require a "lazy" prefix at the  
 calling point too. This little syntax problem that was already raised  
 during the D2 design phase.

I would like to clarify my point: Inarguably (IMO), the original intention of lazy parameters was to allow passing expressions to functions that were evaluated only when required. David's library evaluates these expressions *in different threads*! Generally, I would count that any usage of "lazy" counts as abuse except for evaluating the argument, at most once, during the function's execution. -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Apr 19 2011
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 19 Apr 2011 19:47:57 -0400, Vladimir Panteleev  
<vladimir thecybershadow.net> wrote:

 On Wed, 20 Apr 2011 02:43:50 +0300, bearophile  
 <bearophileHUGS lycos.com> wrote:

 I agree. In my opinion the problem is not you using in your module a  
 language feature designed for that very purpose. What's in question is  
 the syntax of that language feature. I don't mind lazy arguments, but  
 it can be argued that's better for them to require a "lazy" prefix at  
 the calling point too. This little syntax problem that was already  
 raised during the D2 design phase.

I would like to clarify my point: Inarguably (IMO), the original intention of lazy parameters was to allow passing expressions to functions that were evaluated only when required. David's library evaluates these expressions *in different threads*! Generally, I would count that any usage of "lazy" counts as abuse except for evaluating the argument, at most once, during the function's execution.

Lazy is also good at abstracting error handling. For example, I use lazy to perform smart C error code checking, which allows me to handle certain common errors and then re-try the function.
Apr 19 2011
prev sibling next sibling parent Kagamin <spam here.lot> writes:
Vladimir Panteleev Wrote:

 One classic example of this is passing arguments by  
 reference, something D inherited from C++. For example:
 
 int x, y;
 // ...
 someObject.updateCoords(x, y);
 
 What can you say about this code?

D IS TEH typesaving language. Go the D way or go away :3
Apr 19 2011
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Wed, 20 Apr 2011 07:37:14 +0300, Kagamin <spam here.lot> wrote:

 D IS TEH typesaving language. Go the D way or go away :3

I think The D Way is to be a debugsaving language before everything else... ;) -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Apr 20 2011
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
On 20/04/2011 00:09, Vladimir Panteleev wrote:
 To elaborate, I mean allowing code which appears to behave surprisingly
 different from the at-a-glance interpretation, unless the programmer
 knows the function's signature. I've noticed a worrying adoption in D of
 this "antipattern", which, frankly, I believe doesn't belong in a
 well-designed programming language. One classic example of this is
 passing arguments by reference, something D inherited from C++. For
 example:

 int x, y;
 // ...
 someObject.updateCoords(x, y);

 What can you say about this code? The unobvious and surprising
 interpretation of it is that updateCoords will change the values of x
 and y. C# solved this problem neatly by requiring to specify the "ref"
 keyword before the function arguments:

 someObject.updateCoords(ref x, ref y); // much clearer

 This problem carries over to lazy parameters, as well. I'll quote a line
 of code from a recent post by David Simcha:

This problem is also very similar to the named arguments problem (figuring out which argument corresponds to which parameter when there are many parameters and they all have the same type). My response is the same as the named arguments issue: I think it is preferable (if not preferable, then at least sufficiently good) to use an IDE to look up the full function signature, than to make language additions to make the call clearer by just looking at the source text. Your post actually adds a bit more value to my approach, because it shows my approach is the same no matter what you want to figure out about the arguments (if they are ref, lazy, to which named parameter the argument corresponds, etc.), and it also brings up the full DDoc with the full information describing what the function does. Conversely, your approach does not scale in a sense, because you need to keep adding stuff for each new thing you want to know about the parameter (ref, lazy, named argument, etc.). Should we add variadic parameters to the mix as well? What else? Taking this approach to the extreme, we might as well replicate all the information in the function signature in the function call... -- Bruno Medeiros - Software Engineer
Apr 21 2011