Functions

The Behavior of Smart Contracts

We saw in the Contract Structure section that methods are what allows for contracts to be decentralized programs that they are. However, we have yet to explore what methods (functions) consist of.

Function Headers

Although one may want to dive right into the logic of a function itself, an equally important task is defining the function header itself. In Solidity, all function headers begin with the function keyword which tells the compiler that we are defining a function. Afterwards, we declare the name of the function itself, followed by brackets

function print()

In the code snippet above, we are defining a function named print.

What About Function Parameters?

All function parameters live within the brackets next to the function name. As an example, if we wanted print to take in an unsigned integer, we would modify our function header to the following:

function print(uint num)

If we wanted our function to take in multiple parameters, we would modify our function header as follows:

function print(uint numOne, int numTwo)

After the function keyword, function name, and optional parameters, the last necessary section of the function header which we must define is the visibility of the function.

All smart contracts live in a distributed system, and one of the challenges this presents is how to make sure only certain actors can call certain functions. For this, Solidity introduces the concept of visibility; listed below are the four types of visibility available to functions:

  • Public: any actor can call a public function

  • Private: only the contract itself can call a private function

  • Internal: only the contract itself and any child contract can call an internal function

  • External: the contract itself and child contracts cannot call an external function; other actors can call external functions.

Below, we see visibility being used in (syntactically correct) function headers:

function print() public {}
function print() private {}
function print() internal {}
function print() external {}

Extensions of Function Headers

All function headers are required to have the function keyword, a function name, and a specified visibility. However, the following questions demonstrate what our current understanding of function headers is missing:

  • How do we define return values?

  • What if we can guarantee that our function does not modify or even view the state of the blockchain?

We begin by tackling the idea of defining return values. At the end of the function header, if we want our function to return a value, we append the return keyword to the function header and in parentheses, declare the return types of the values the function will return. An example of returning values from a function can be seen below:

function getNum() public returns (uint) {}

Like function parameters, we are not limited to just a single return value; functions in Solidity are capable of returning multiple values:

function getNums() public returns (uint, int) {}

Focusing now on the mutability guarantees of a function, we can add mutability keywords to a function header if a function meets certain criterias:

  • View: the function at most only views the state of the blockchain

  • Pure: the function does not view the state of the blockchain at all

As an example, consider the Rectangle contract from contract structure:

contract Rectangle {

    uint256 length;
    uint256 width;

}

Now consider the following two functions we wish to add to Rectangle:

  • getFive: returns the number 5

  • getArea: returns the area of the rectangle

Notice that in the case of getFive, we can declare this function as being pure since this does not require us to view the state of the blockchain in any manner. For getArea, we can mark it as view since we are viewing the state of the blockchain (via using the fields length and width) but we cannot mark it as pure:

contract Rectangle {

    uint256 length;
    uint256 width;
    
    function getFive() public pure returns (uint256) {
        return 5;
    }
    
    function getArea() public view returns (uint256) {
        return length * width;
    }

}

One interesting observation is that all pure functions can be marked as view, but not all view functions can be marked as pure.

Function Body

The function body is where the logic of a function lives. Like state variables, each line within the function body must end with a semicolon.

Last updated