Personally, for this reason alone, I think no language should allow more than 1 parameter of the same type for functions definitions and invocations UNLESS it supports and requires named parameters. This is a HUGE source of unintentional, and sometimes late discovered, bugs in software. I think a good rule would be that any function call that isn’t a different type for each parameter (including implicit conversions) should require named parameter calling convention - both for readability and correctness assurance. If a method/function has more than 1 parameter of the same type, then, named parameter calling convention should be 100% required as follows:
fn foo ( a : i32 );
fn bar ( a : i32, b : &str );
fn foobar ( a: i32, b: &str, c: i32 );
foo ( 3 ); // OK
foo ( a : 3 ) // OK
bar ( 3, "Hello" ); // OK
bar( a : 3, b : "Hello" ); // OK
foobar( 3, "Hello", 5 ) // Compiler Error/Not Permitted
foorbar( a: 3, b : "Hello", c : 5 ) // OK
Alternatively, if you don’t want to allow “Named Parameter Calling Convention”, then, just don’t allow methods/functions to be defined with more than one parameter of the same type. That is, require the developer to create a struct to handle necessary values and only have one parameter (other than self) to cover all the conflicts. That seems counter-productive though. Better just to support (and require) named parameter calling convention IMHO.
For example, I might define a function\method like this:
fn add_point( self : &MyStruct, x, y : i32 ) -> ();
Today, that would be called like this:
s.add_point( 3, 5 );
Now, being that “Points” have a clearly accepted ordering for x/y, this seems clear enough, right? But, what if the create author for “point” did it the other way around? Are you sure you got it right? I know, I know, unlikely scenario for something as simple and well-defined as “Point”, but, that’s the simplest, least likely thing like this to have this problem. Many things are much, much, much more ambiguous. How about this?
fn frobnicate( self : &MyType, a : i32, b : &str, c : f64, d : f64, e : i32, f : i32, g : &str ) -> ();
...
s.frobnicate( 3, "Hello", 3.14, 7.67, 5, 7, "What did you do? You broke it!" );
Did I call that right? Are the parameters lining up correctly. Should I have swapped 5 & 7? Shoot, I have to double-check the docs. It will compile, but, might not WORK CORRECTLY.
Now this:
s.frobnicate( a : 3, b : "Hello", c : 3.14, d : 7.67, e : 5, f : 7, g : "Good-Bye" );
Would be much easier for me to spot that I’ve called the function correctly. Not only that, but, this would also work and be easy to spot that it is or isn’t correct:
s.frobnicate( c : 3.14, d : 7.67, a : 3, b : "Hello", e : 5, f : 7, g : "EPIC FAIL!" );
This is even more apparent if the parameters have good names:
fn frobnicate( self : &MyType,
default_value : i32,
greeting : &str,
min : f64,
max : f64,
warn_min : i32,
warn_max : i32,
error_msg: &str ) -> ();
Which, when called under these requirements looks like this:
s.frobnicate( default_value : 6,
greeting : "Hello There",
min : 3.05,
max : 9.62,
warn_min : 4,
warn_max : 8,
error_msg: "YOU HAVE JUST MADE A FATAL MISTAKE! I HOPE YOU KNOW SOMETHING ABOUT HAND-TO-HAND COMBAT!" );
or even this (order doesn’t matter):
s.frobnicate( default_value : 6,
min : 3.05,
max : 9.62,
warn_min : 4,
warn_max : 8,
greeting : "Hello There",
error_msg: "YOU HAVE JUST MADE A FATAL MISTAKE! I HOPE YOU KNOW SOMETHING ABOUT HAND-TO-HAND COMBAT!" );
Wouldn’t you rather READ the above than this?:
s.frobnicate( 6,
"Hello There",
3.05,
9.62,
4,
8,
"YOU HAVE JUST MADE A FATAL MISTAKE! I HOPE YOU KNOW SOMETHING ABOUT HAND-TO-HAND COMBAT!" ) ;
I really can’t understand how anyone would ever prefer positional syntax for something like this. It’s error-prone, difficult to read, and requires substantially more mental gymnastics to keep straight. I HATE code like this. Unfortunately, you come across it all the time. Why not just make it not allowed?