A downside or two of function keyword arguments (and default values)

August 18, 2024

Recently I read An unordered list of things I miss in Go (via). One of the things is 'keyword and default arguments for functions', to which I had a reaction:

Hot take: having keyword arguments and optional arguments (and default values) for function calls in your language encourages people to make functions that take too many arguments.

(I can see why Python did it, because on the one hand class/object construction and on the other hand, in a dynamic language it lets you change a function's API without having to hunt down absolutely everyone who calls it and now doesn't have that extra argument. Just make the new argument a keyword one, done.)

Technically you can have keyword arguments without supporting optional arguments or default values, but I don't think very many languages do. The languages I've seen with this generally tend to make keyword arguments optional and then sometimes let you set a default value if no value is supplied (otherwise an unsupplied argument typically gets a zero value specific to its type or the language; for example I believe Emacs Lisp makes them all nil).

I'm sure it's possible to make tasteful, limited use of keyword arguments to good benefit; the article suggests one (in Go) and I'm sure I've seen examples in Python. But it's far more common to create sprawling APIs with a dozen or more parameters, such as Python's subprocess.run() (or the even larger subprocess.Popen()). I'm personally not a fan of such APIs, although this is one of those taste issues that is hard to quantify.

(The usual excuse is that you don't normally use all that many of those keyword arguments. But if you do, things are messy, and the API is messy because all of them exist. There are other patterns that can be used for APIs that intrinsically have lots of options; some are common Go practice. These patterns are more verbose, but in my view that verbosity is not necessarily a bad thing.)

Another unfortunate aspect of optional keyword arguments with default values is that they enable a lazy way of expanding and changing function APIs (and method APIs and constructor APIs and so on). Rather than add or change a regular argument and have to update all of the call sites, you add a new keyword argument with a default value, and then only update or add call sites that need to use the new part of the API. I've done this myself because it was quick and easy, and I've also wound up with the end state of this that you sometimes get, where all of my call sites were using the new API with the new keyword argument, so I could have gotten rid of it as a keyword and made it a regular argument.

I also feel that keyword arguments encourage a certain bad way of evolving APIs (by making this way the easiest way to move forward). In one version, some bad behavior is allowed to linger because everyone is supposed to know to turn it off with a keyword argument. In another version, some incompatibility is eventually allowed to be added because everyone who doesn't want it is supposed to have used a keyword argument to disable it. In either situation, if you don't know about the magic trick with the keyword argument, you lose out.

What you can say for keyword arguments is that taking a lot of keyword arguments is better than taking a lot of non-keyword arguments, but to me this is not much of an endorsement. It's better not to have lots of arguments in general.


Comments on this page:

By lilydjwg at 2024-08-18 19:53:36:

Technically you can have keyword arguments without supporting optional arguments or default values

Python has this, i.e. def f(*, a, b)

I don't think having a lot of keyword arguments is very different than the builder pattern where you build a configuration object by a lot of method calls. Sometimes you have to support that kind of things, either by keyword arguments, or builder pattern, or building multiple different objects (e.g. a Request object containing a Url object, a Headers object and an optional HTTPBody object).

By Rob at 2024-08-19 04:55:47:

My hot take: functions with more than one argument should only take keyword arguments.

This eliminates ambiguity, which makes code review easier, as well as making it harder to accidentally mismatch arguments (especially for languages without strict typing, or if multiple args are the same type).

By cks at 2024-08-19 10:48:58:

What I meant by having keyword arguments without default values and optional arguments was as a language feature; this would be a language where arguments could be named instead of positional, but all arguments were always required. In a way this is a version of Rob's hot take. I don't know if any commonly used programming language actually does this.

I think this is very close to passing an interface argument and having optional members in the interface, which is already easy to do in Go

Ada is a fascinating language in this context, because it lacks optional parameters, but keyword parameters are available for every subprogram without exception. All parameters have names, and Ada allows the programmer to use positional arguments, or their names, after which the names must be used, of course. Unlike Common Lisp, there's no possible efficiency penalty here, because it's all determined at compilation anyway.

Ada supports default values for subprogram parameters, but only for the in parameters, for which only their values matter. For functions named by operators such as "+" the names don't matter unless a clumsy prefix syntax be used, but the option's present. Ada, however, allows for overloading, making it very easy to design subprograms distinguished only by their types, modes, or counts of parameters; for functions, they can also be overloaded exclusively by the returned value's type.

In Ada, there aren't usually problems with bad interface designs, because the language encourages doing a good job in planning, something few other languages do.

Written on 18 August 2024.
« Why and how I keep around spare libvirt based virtual machines
It's not simple to add function keyword arguments to Go »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Sun Aug 18 15:28:46 2024
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.