In this article, we’ll entertain ourselves by learning about an interesting topic: positional and named function calls in Solidity.
It’s part of our long-standing tradition to make this (and other) articles a faithful companion and supplement to the official Solidity documentation.
Function Parameters and Arguments
So far, we’ve learned about internal and external function calls, but we haven’t talked much about function call arguments and parameters.
As I’ve pointed out before, and will gladly make a reminder from time to time, there are two perspectives from which we can look at functions, even regardless of the programming language. They are both concerned with the function’s input points:
- Function Parameters
- Function Arguments
Let’s shortly examine both next. π
Function Parameters
The first perspective starts with a function definition or declaration (both terms work just fine).
When defining a function, we might give it zero or more input points (up to 16 in theory, but less in practice, see the reminder below), and these input points link our function to its outer world, or more technically, to the outer context of the function.
We refer to these input points of function as parameters. In computer science terminology, parameters are also called formal parameters, but you’ll rarely hear anyone referring to them as anything other than just – parameters.
Function Arguments
The second perspective takes a look at an already-defined function being used during a function call.
In this case, we’re passing some values to the function as a part of its call. These values are positionally aligned to our predefined input parameters, and from this perspective, we’re referring to them as arguments.
In computer science terminology, arguments are also called actual parameters. Most of the time, you’ll just hear the term “arguments”, sometimes even mistakenly interchanged with the term “parameters”. The two are not the same, and we’ll show why in a moment.
π§ Reminder: Since the Ethereum Virtual Machine (EVM) is not a register machine, but a stack machine, it is important to know how many elements we can access from the stack at any given time. It is possible to directly access and copy one of the topmost 16 elements to the top of the stack. These 16 elements represent a theoretical limit for a number of parameters in a function. In reality, we should expect an even lower number of parameters than 16, however, never more than 16. This arrangement influences the theoretical maximum of function parameters and return variables.
Positional Function Call
First of all, let’s take a simple example of a function definition. We’ll show how we used to use it so far and compare this approach with an alternative one.
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.0 <0.9.0; contract C { mapping(uint => uint) data; function f() public { set(2, 3); } function set(uint key, uint value) public { data[key] = value; } }
Notice how the set(...)
function is defined: it takes two parameters, a key
of type uint
and a value
of type uint
.
These two parameters will receive the arguments passed from the outside and store a key/value pair in the mapping data declared at the beginning of the contract. There are three main takeaways from this example:
- Parameters denote two variables belonging to the function definition;
- Arguments denote two values belonging to the function call;
- Arguments are passed to parameters based on their position.
Named Function Call
We’ll take a look at a very similar example, but this time, we’ll pass the arguments to the parameters by the parameter names in the f()
function calling the set(...)
function:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.0 <0.9.0; contract C { mapping(uint => uint) data; function f() public { set({key: 3, value: 2}); } function set(uint key, uint value) public { data[key] = value; } }
By focusing on the f()
function, which calls the set(...)
function, we notice a novelty in how the arguments are passed to the function parameters: the arguments are passed by associating them with parameter names.
That’s the way of doing the named call of a function. One big advantage is that we don’t have to pay attention to the order in which the parameters appear in the function definition; the mapping will take care of it.
Positional vs. Named Function Call
Although noticeable from comparing the two examples, I’ll point out the crucial difference: when doing the named function call, technically speaking, we’re passing only one parameter, a mapping.
The mapping contains the keys (they represent parameter names) and values (they represent our arguments).
Let’s summarize the difference:
- The positional function call:
set(2, 3);
- The named function call:
set({key: 3, value: 2});
The named function call implies that we’re passing our arguments to the underlying function parameters by their names, and not by the parameter position. In that sense, we can distinguish a named function call and a positional function call (the default approach).
π‘ Note: On a personal note, being a programmer for quite some time, I prefer the old-school way, i.e. passing arguments to function parameters via positions. The reason for my preference is two-fold.
- The first reason lies in the fact that the positional function call is more telling in terms of the changed function signature. In other words, if the underlying function signature changes, we’ll get slapped with an error informing us of the newly-introduced mismatch.
- The second “reason” is, it’s a matter of habit and homage to my beginnings in the universe of computers (yeah, even seasoned programmers get nostalgic sometimes).
Positional function call also involves a more thorough refactoring of the code, but take my word for it: you’ll want to know if something changed within your project, especially when you’re building a solution that involves someone else’s functions, modules, libraries, etc.
Conclusion
In this article, we learned what function parameters and arguments are, and also learned about the two distinctive ways of calling a function: via a positional function call and a named function call.
First, we introduced the notion of two perspectives of looking at a function’s input points: from the inside and from the outside. At the same time, we learned what function parameters and arguments are.
Second, we discovered that we usually call our functions through a positional function call.
Third, we learned what a named function call is and how to use it.
Fourth, we made a comparison between the two and learned about my personal preference for a function call. Now, it’s up to you to learn about your own.
What’s Next?
This tutorial is part of our extended Solidity documentation with videos and more accessible examples and explanations. You can navigate the series here (all links open in a new tab):