14

I was just thinking how much easier it would be to read code if, when calling a function, you could write:

doFunction(param1=something, param2=somethingElse);

I can't think of any drawbacks and it would make code a lot more readable. I know you could pass an array as the only argument and have the array keys as the parameter names, however that would still not be as readable.

Is there a disadvantage of this that I'm missing? If not, why do many languages not allow this?

closed as primarily opinion-based by gnat, user53019, Bart van Ingen Schenau, Kilian Foth, World Engineer Dec 3 '13 at 5:40

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    can you give a language that should (and can) have named parameters but don't have it? – Bryan Chen Nov 26 '13 at 22:42
  • 1
    I think it's too verbose in many cases. As far as I've seen, whether a language uses this or not tends to depend on whether it would impact the language positively or negatively. Most C-style languages do well without it. In their case, it's often quite obvious what's going on anyway, and its absence really does help to just reduce clutter in general. – Panzercrisis Nov 26 '13 at 22:55
  • 2
    @SteveFallows: The "Named Parameter Idiom" seems like a strange name for definition of a fluent API (as named by Martin Fowler) on a builder class. You can find examples of the same pattern in one of the first items of Joshua Bloch's Effective Java. – jhominal Nov 26 '13 at 23:02
  • 3
    This is not necessarily an opinion based question – Catskul Jun 17 '14 at 23:59
  • 2
    Agreed. "Why do many languages not support named parameters?" is more a question of history of languages than opinion. It would be opinion if someone asked it as "Aren't named parameters better?". – Andy Fleming Nov 17 '15 at 9:27
14

Specifying parameter names does not always make code more readable: as an example, would you prefer to read min(first=0, second=1) or min(0, 1)?

If you accept the previous paragraph's argument, then it is pretty obvious that specifying parameter names should not be mandatory. Why don't all languages make specifying parameter names a valid option?

I can think of at least two reasons:

  • In general, introducing a second syntax for an already-covered need (here, passing parameters) is something whose benefits must be balanced against the inherent cost of the introduced language complexity (both in language implementation and in the programmer's mind model of the language);
  • It makes changing parameter names an API breaking change, which may not conform to a developer's expectation that changing a parameter name is a trivial, non-breaking change;

Note that I do not know of any language that implements named parameters without also implementing optional parameters (which require named parameters) - so you should be wary of overestimating their readability benefits, which can be more consistently obtained with the definition of Fluent Interfaces.

  • 6
    I regularly code in several different languages, I nearly always have to look up the parameters for a simple substring in any given language. Is it substring(start,length) or substring(start,end) and is end included in the string? – James Anderson Nov 27 '13 at 1:19
  • 1
    @JamesAnderson - How would a named-parameter substring solve you issue? You still would have to look up the parameters in order to know what the name of the second parameter is (unless both functions exist and polymorphism feature of the language allows same type parameters with different names). – mouviciel Nov 27 '13 at 8:07
  • 6
    @mouviciel: Named parameters aren't much use at all for the writer, but are fantastic for the reader. We all know how important variable names are for making readable code, and named parameters allow the creator of an interface to give good parameter names as well as good function names, making it easier for people using that interface to write readable code. – Phoshi Nov 27 '13 at 9:32
  • @Phoshi - You are correct. I just forgot the importance of reading code when I wrote my previous comment. – mouviciel Nov 27 '13 at 10:24
14

Named Parameters Make Code Easier To Read, Harder To Write

When I am reading a piece of code, named parameters can introduce context that makes the code easier to understand. Consider for example this constructor: Color(1, 102, 205, 170). What on earth does that mean? Indeed, Color(alpha: 1, red: 102, green: 205, blue: 170) would be far easier to read. But alas, the Compiler says “no” – it wants Color(a: 1, r: 102, g: 205, b: 170). When writing code using named parameters, you spend an unnecessary amount of time looking up the exact names – it is easier to forget the exact names of some parameters than it is to forget their order.

This once bit me when using a DateTime API that had two sibling classes for points and durations with almost identical interfaces. While DateTime->new(...) accepted an second => 30 argument, the DateTime::Duration->new(...) wanted seconds => 30, and similar for other units. Yes, it absolutely makes sense, but this showed me that named parameters ≠ ease of use.

Bad Names Don't Even Make It Easier To Read

Another example how named parameters can be bad is probably the R language. This piece of code creates a data plot:

plot(plotdata$n, plotdata$mu, type="p", pch=17,  lty=1, bty="n", ann=FALSE, axes=FALSE)

You see two positional arguments for the x and y data rows, and then a list of named parameters. There are many more options with defaults, and only those are listed whose defaults I wanted to change or explicitly specify. Once we ignore that this code uses magic numbers, and could benefit from using enums (if R had any!), the issue is that many of these parameter names are rather indecipherable.

  • pch is actually the plot character, the glyph that will be drawn for every data point. 17 is an empty circle, or something like that.
  • lty is the line type. Here 1 is a solid line.
  • bty is the box type. Setting it to "n" avoids a box being drawn around the plot.
  • ann controls the appearance of axis annotations.

For someone who does not know what each abbreviation means, these options are rather confusing. This also reveals why R uses these labels: Not as self-documenting code, but (being a dynamically typed language) as keys to map the values to their correct variables.

Properties Of Parameters And Signatures

Function signatures may have the following properties:

  • Arguments can be ordered or unordered,
  • named or unnamed,
  • required or optional.
  • Signatures can also be overloaded by size or type,
  • and can have an unspecified size with varargs.

Different languages land at different coordinates of this system. In C, arguments are ordered, unnamed, always required, and can be varargs. In Java the situation is similar, except that signatures can be overloaded. In Objective C, signatures are ordered, named, required, and can't be overloaded because it's just syntactic sugar around C.

Dynamically typed languages with varargs (command line interfaces, Perl, …) can emulate optional named parameters. Languages with signature size overloading have something like positional optional parameters.

How Not To Implement Named Parameters

When thinking of named parameters, we usually assume named, optional, unordered parameters. Implementing these is difficult.

Optional parameters could have default values. These must be specified by the called function and should not be compiled into the calling code. Otherwise, the defaults can't be updated without recompiling all dependent code.

Now an important question is how the arguments are actually passed to the function. With ordered parameters, the args can be passed in a register, or in their inherent order on the stack. When we exclude the registers for a moment, the problem is how to put unordered optional arguments onto the stack.

For that, we need some order over the optional arguments. What if the declaration code is changed? As the order is irrelevant, a reordering in the function declaration should not change the position of the values on the stack. We should also consider if adding a new optional parameter is possible. From a users perspective this seems so, because code that didn't use that parameter previously should still work with the new parameter. So this excludes orderings like using the order in the declaration or using the alphabetic order.

Consider this also in the light of subtyping and the Liskov Substitution Principle – in the compiled output, the same instructions should be able to invoke the method on a subtype with possibly new named parameters, and on a supertype.

Possible Implementations

If we cannot have a definitive order, so we need some unordered data structure.

  • The simplest implementation is to simply pass the name of the parameters along with the values. This is how named params are emulated in Perl or with command line tools. This solves all extension problems mentioned above, but can be a huge waste of space – not an option in performance-critical code. Also, processing these named params is now much more complicated than simply popping values off a stack.

    Actually, the space requirements can be reduced by using string pooling, which can reduce later string comparisons to pointer comparisons (except when it can't be guaranteed that static strings are actually pooled, in which case the two strings will have to be compared in detail).

  • Instead, we could also pass a clever data structure that works as a dictionary of named arguments. This is cheap on the caller side, because the set of keys is statically known at the call location. This would allow to create a perfect hash function or to precalculate a trie. The callee will still have to test for existence of all possible parameter names which is somewhat expensive. Something like this is used by Python.

So it's just too expensive in most cases

If a function with named parameters is to be properly extensible, a definitive ordering cannot be assumed. So there are only two solutions:

  • Make the order of the named params part of the signature, and disallow later changes. This is useful for self-documenting code, but does not help with optional arguments.
  • Pass a key-value data structure to the callee, which then has to extract useful information. This is very expensive in comparision, and usually only seen in scripting languages without an emphasis on performance.

Other Pitfalls

The variable names in a function declaration usually have some internal meaning and are not part of the interface – even if many documentation tools will still show them. In many cases you'd want different names for an internal variable and the corresponding named argument. Languages that don't allow choosing the externally visible names of a named parameter don't gain much of them if the variable name isn't used with the calling context in mind.

An issue with emulations of named arguments is the lack of static checking on the caller side. This is especially easy to forget when passing a dictionary of arguments (looking at you, Python). This is important because passing a dictionary is a common workaround, e.g. in JavaScript: foo({bar: "baz", qux: 42}). Here, neither the types of the values nor the existence or absence of certain names can be checked statically.

Emulating Named Parameters (In Statically Typed Languages)

Simply using strings as keys, and any object as a value is not very useful in the presence of a static type checker. However, named arguments can be emulated with structs, or object literals:

// Java

static abstract class Arguments {
  public String bar = "default";
  public int    qux = 0;
}

void foo(Arguments args) {
  ...
}

/* using an initializer block */
foo(new Arguments(){{ bar = "baz"; qux = 42; }});
  • 3
    "it is easier to forget the exact names of some parameters than it is to forget their order"... that's an interesting assertion. – Catskul Jun 17 '14 at 23:57
  • 1
    The first counter-example is not a problem with named parameters, and more a flaw with how English plurals map to field names in interfaces. The second counter-example is similarly a problem with developers making bad naming choices. (No choice of parameter passing interface is by itself going to be a sufficient condition for readability - the question is whether or not it is a necessary condition or aid towards it in some cases). – mtraceur Sep 20 '18 at 18:21
  • 1
    +1 for the overall points about implementation costs and tradeoffs to doing named parameters. I just think the beginning will lose people. – mtraceur Sep 20 '18 at 18:28
  • @mtraceur You do have a point. Now, 5 years later, I'd probably write the answer quite differently. If you want to you can edit my answer to remove less helpful stuff. (Usually such edits would be rejected as they contradict the author's intent, but here I'm fine with it). E.g. I now believe that many named parameters problems are just restrictions of dynamic languages, and they should just get a type system. E.g. JavaScript has Flow and TypeScript, Python has MyPy. With proper IDE integration that eliminates most usability problems. I'm still waiting for something in Perl. – amon Sep 22 '18 at 19:56
7

For the same reason that Hungarian Notation is no longer widely used; Intellisense (or its moral equivalent in non-Microsoft IDE's). Most modern IDE's will tell you everything you need to know about a parameter by merely hovering over the parameter reference.

That said, a number of languages do support named parameters, including C# and Delphi. In C#, it is optional, so you don't have to use it if you don't want to, and there are other ways to specifically declare members, like object initialization.

Named Parameters are mostly useful when you only want to specify a subset of optional parameters, and not all of them. In C#, this is very useful because you no longer need a bunch of overloaded methods to provide the programmer with this flexibility.

  • 4
    Python uses named parameters, and it is a wonderful feature of the language. I wish more languages used it. It makes default arguments easier as you are no longer forced, as in C++, to explicitly specify any arguments "before" the defaults. (As you have in your last paragraph, it is how python gets away with having no overloading...default arguments and name arguments let you do all the same things.) Named arguments also make for more readable code. When you have something like sin(radians=2), you don't need "Intellisense". – Steven Burnap Nov 27 '13 at 6:38
2

Bash certainly supports this style of programming, and both Perl and Ruby support passing name/value parameter lists (as would any language with native hash/map support). Nothing stops you from choosing to define a struct/record or hash/map which attaches names to parameters.

For languages which include hash/map/keyvalue stores natively, you can get the functionality you want, simply by adopting the idiom to pass key/value hashes to your functions/methods. Once you have tried it on a project, you will have better insight into whether you have gained anything, either from increased productivity derived from ease of use, or improved quality through reduced defects.

Note that experienced programmers have become comfortable with passing parameters by order/slot. You may also find that this idiom is only valuable when you have more than a few arguments (say >5/6). And since a large number of arguments is often indicative of (overly) complex methods, you may find that this idiom is only beneficial for the most complex of methods.

2

I think it's the same reason why c# is more popular than VB.net. While VB.NET is more "readable" eg you type "end if" instead of a closing bracket, it just ends up being more stuff that pads out the code and eventually makes it harder to understand.

It turns out that what makes code easier to understand is conciseness. The less the better. Function parameter names are normally pretty obvious anyway, and don't really go that far into helping you understand code.

  • 1
    I vehemently disagree that the "less the better". Taken to its logical extreme, code golf winners would be the "best" programs. – Riley Major Oct 11 '16 at 20:44
2

Named parameters are a solution to a problem in refactoring source code and not intended to make source code more readable. Named parameters are used to assist the compiler/parser in resolving default values for function calls. If you want to make code more readable, than add meaningful comments.

Languages that are difficult to refactor often support named parameters, because foo(1) is going to break when the signature of foo() changes, but foo(name:1) is less likely to break requiring less effort from the programmer to make changes to foo.

What do you do when you have to introduce a new parameter to the following function, and there are hundreds or thousands of lines of code calling that function?

foo(int weight, int height, int age = 0);

Most programmers will do the following.

foo(int weight, int height, int age = 0, string gender = null);

Now, no refactoring is required and the function can execute in legacy mode when gender is null.

To specific gender a value is now HARDCODED into the call for age. As this example:

 foo(10,10,0,"female");

The programmer looked at the function definition, saw that age has a default value of 0 and used that value.

Now the default value for age in the function definition is completely useless.

Named parameters allow you to avoid this problem.

foo(weight: 10, height: 10, gender: "female");

New parameters can be added to foo and you don't have to worry about the order they were added in and you can change default values knowing that the default you set is really it's true default value.