Code Complete: Explicit and Implicit Function Parameters

Show Full Version

In dynamic languages, there are two main approaches when choosing the input parameters for functions: the first is to use explicit, positional arguments, and the second is to pass a structure containing everything that the function expects. In real code, passing arguments implicitly and explicitly are equally common, and it’s not always clear which one should be chosen for a given function. That's what we're going to look at today.

Let's start with a simple example, a function that finds the highest integer among passed:

// Explicit parameters
const max1 = (n1, n2) => (n1 > n2 ? n1 : n2);
max1(1, 2); // 2

// Implicit parameters
const max2 = ({ n1, n2 }) => (n1 > n2 ? n1 : n2);
max2({ n1: 1, n2: 2 }); // 2

Explicit parameters make the code self-documenting: the function signature immediately indicates which arguments are expected, and the good names suggest their types. The apply method makes it much easier to call the function if the parameters are stored in an array:

// Built-in js function
Math.max(3, 2); // 3

const numbers = [3, 2];
Math.max.apply(null, numbers);
// Instead of Math.max(numbers[0], numbers[1]);

Now, let's talk about the drawbacks. First and foremost, the function is too argument-dependent. If you change the number of arguments, including adding and removing default parameters, you must then rewrite all function calls. This is especially true for high-level functions that handle a large amount of data:

// The user can have a dozen or two fields
// Some of them are optional
// All of this could change during the course of the program
// Rigid tie for the order, although in the sense of the order there is no
const user = new User(firstName, lastName, email);
user.save();

When implicit arguments are used, the situation changes. Self-documentation level decreases dramatically. It's hard to sort out what can be passed to a function without first describing or examining its contents. The definition of such functions, on the other hand, rarely changes since the number of inputs is increased automatically:

const data = { firstName, lastName, email };
// The order is no longer important
// Any data can be passed, provided the function can handle it
const user = new User(data);
user.save();

Which option is preferable in a given situation? According to the previous, explicit arguments work best with simple and low-level functions, such as mathematical ones. As a result, all functions that work with strings or numbers in any language have explicit arguments. Almost every other built-in function and method is the same.

This also applies to users’ functions with a clear, rarely changing structure and a limited number of arguments.

// A function that writes data to a file
writeFile('path/to/file', 'data');

Implicit arguments are a little more complicated. Here is when it’s better to use them:

In some languages, such as Python or Ruby, there are special named parameters. They are explicitly stated as positional parameters in the definition, making them self-documenting. However, they can be provided in any order by specifying the parameter name when called. This method does not replace the others, but it does simplify the code in some cases.

But not all situations can be decomposed so easily. If you're not sure what to do, start with explicit arguments and only then move to implicit ones if necessary. Common sense should be your main guideline.

Additional materials: