Expressions and Control Structures in Solidity Smart Contracts
In this article, we’ll talk about omitted function parameter names, i.e. what they are, when and why we should use them, and how they make our programming life more interesting.
It’s part of our long-standing tradition to make this (and other) articles a faithful companion, or a supplement to the official Solidity documentation.
Why Omitting Function Parameter Names?
So far, we’ve emphasized function calls, i.e. external and internal function calls.
π Recommended Tutorial: Solidity Internal and External Function Calls
We’ve also discussed positional and named function calls, in terms of passing the arguments to a called function.
π Recommended Tutorial: Solidity Named vs Positional Function Calls
However, all this talk was about functions in their common form, i.e. about functions with named parameters. Of course, our parameters always had names because we needed them to forward the appropriate arguments through parameters to the function and let the function do its magic.
After all, that’s what the functions are for; they are defined with a particular goal: to carry out a certain, well-defined functionality.
But there are specific, maybe (or not) so frequent cases where we need a function with parameters that aren’t named. I know, it might sound strange, and it’s probably even tricky to imagine such a situation at the moment, but let me give you a hint about something we just mentioned: the key is in “we need to have a function“.
This expressed “need” to have a function stems from the fact that, depending on project goals, we may be working on a functionality design guided by a standard.
This standard, being in a freely written form, or more often, expressed as a source code or a reference implementation, outlines in what form we should implement our new functionality to ensure a required degree of compliance with the existing software.
We may still ask ourselves, why would we want to follow a standard? Why wouldn’t we just code a project guided by our own imagination, experience, and best effort, and follow the adventure wherever it takes us?
Sure, we could, but here’s a catch: if we follow a standard while coding a project, we can rest assured that we’d be able to connect our project with another project, system, infrastructure, contract, or just use some library following the same standard. Simply put, we’ll enable our project to communicate with existing and future participants because we designed and implemented it in a standardized, well-defined way.
A common, but not the only way to ensure such standardization is by using interfaces.
We’ll talk about interfaces in more detail in the later articles, but I mentioned one form of possible instruments that enforce software components design, i.e., function design in a particular way.
π‘ Note: We can conceptually imagine interfaces as agreements between our software and the outside world. By following these agreements, we ensure that our software can communicate with others that follow it too.
Besides interfaces, one more instrument that drives software interoperability is class inheritance. Since Solidity doesn’t use classes as such but works with contracts, contract inheritance is an instrument at our disposal, and also a topic for one of the future articles.
In summary, interfaces and inherited contracts prescribe how a function must look like. Anonymous parameters are a way of making the function prescription a bit softer, and that’s one incentive for learning about them.
π¬ Personal Note: I must admit, many years ago, while I was learning about interfaces, I had no clue what the author was writing about. I felt like I understood the mechanics, i.e. what the code was doing, but I needed a sensible context of use to grasp the reasoning behind the idea, i.e. the why of the idea. It took me some time and other circumstances to finally understand it’s all about interoperability, or if you like, compatibility. That experience also made me realize that, regardless of the topic, it is valuable when the idea behind a concept is revealed first, and only then the dive into the mechanics of a subject is taken.
Omitted Function Parameter Names in Action
In this section, we’ll go through two examples demonstrating how we can omit the parameters.
The situation also demands that we touch on interface implementation and contract inheritance, but we’ll do so as little of that as needed because there’s more to say in future articles. Also, we don’t want to divert our attention from our main topic.
The Interface Implementation Example
We’re about to see how to omit a function parameter name while implementing a function specified by an interface.
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; /* * @title Demonstration of Omitted Function Parameter Names * in the context of interface implementation. */
This is the interface that defines how our function should look like.
Think of it as a standard we want to adhere to in order to keep our contract standardized. It enforces us to have a function called sumThemUp(...)
, which takes two arguments of type int
and returns an int
.
interface ContractInterface { function sumThemUp (int a, int b) external returns (int); }
This contract implements the interface and, by adhering to the interface limitations, it must have a function called sumThemUp(...)
.
However, we have been silently given a bit of freedom to play around with the parameter names, so we’ll omit one of them because we don’t need it. Both arguments will still be passed to our function and to the call stack, but we won’t be able to access the second parameter because it doesn’t have a name.
The interface just prescribes the function signature, but we can define the function’s behavior in any imaginable way, as long as it returns an int
.
contract ImplementedInterface is ContractInterface {
The function has the same name and simple behavior: it just returns the input parameter.
function sumThemUp(int a, int) override pure external returns (int) { return a; } }
The Contract Inheritance Example
We’re about to see how to omit the function parameter name while overriding a function.
In contrast to using an interface for outlining the function, i.e. prescribing the function signature, we’re defining a contract that we’ll use both in its original form and as a base (template) contract for other, derived contracts.
π‘ Note: A base contract is the one that other contracts inherit from. A derived contract is a contract that builds on the base contract; it inherits the base contract’s function, but can also override them if needed by defining a new function with the same signature while ignoring the inherited function.
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; /* * @title Demonstration of Omitted Function Parameter Names * in the context of contract inheritance. */
Definition of the base contract BaseContract
.
contract BaseContract {
The function has the same name and simple behavior: it only multiplies the input parameters and returns the result.
π‘ Note: Functions that are allowed to be overridden must be denoted as virtual.
function sumThemUp(int a, int b) virtual external pure returns (int) { return a * b; } }
Definition of a derived contract DerivedFromBase
.
contract DerivedFromBase is BaseContract {
Overrides the function sumThemUp(...)
and omits the name of the second parameter. The function can omit the second parameter because it’s not necessary for the function behavior: multiplication of the first parameter by a constant 3
.
function sumThemUp(int a, int) override external pure returns (int) { return a * 3; } }
Definition of another derived contract EmptyDerivedFromBase
. This contract doesn’t introduce anything new but just demonstrates that this, too, can be done.
contract EmptyDerivedFromBase is BaseContract { }
Conclusion
In this article, we’ve seen how, when, and why we can omit function parameter names in particular scenarios.
First, we learned about the backstory of omitting the function parameter names.
Second, we just scratched the surface of using interfaces and observed the main subject in the context.
Third, we met with the concept of contract inheritance and observed the main subject in the context.