Chapter 6. Functions

Functions are subroutines (sequences of code that preform a specific task) that can be used to calculate or execute specific purposes. This chapter will cover writing global functions, parameter type enforcement for functions, and return type enforcement for functions.

6.1 - Writing Global Functions

Thus far, we have written one global function, and used several built in functions. Every program we wrote has a global main function, and we have instructed this main function to call the println built in global function.

Let's create a simple function that writes Hello, World! to the screen. The name of the function will be sayhello.

func sayHhello () {
    println ("Hello, World!");
}

func main () {
    sayhello ();
    sayhello ();
    sayhello ();
}

Output:

Hello, World!
Hello, World!
Hello, World!

The function sayhello has one statement in it's body, a call to the println function with the argument "Hello, World!". When the main function executes, it makes three calls to sayhello, which in turn makes three calls to println, displaying the message three times to the terminal.

6.1.1 Functions - Naming

Function names must follow the same sets of rules and restrictions as local variable names. Function names must start with a letter or an _ and must not contain any symbols or be a reserved word. It is also best practice to keep function names lowercase.

6.1.2 Functions - Arguments

Functions can accept arguments (sometimes referred to interchangeably as parameters). These are local variables the function has accessed to, which are assigned by the function that calls it. The println function for example takes in an argument and displays it's string value to the screen.

Say we wanted to build a function to calculate and display the product of three numbers. The way a programmer would go about this would be to write a function called multiply that takes in three number parameters and displays the product. Let's do that now.

func multiply (x, y, z) {
    int total = x * y * z;
    println (total);
}

func main () {
    multiply (4, 7, 2);
    multiply (2, 3, 4);
    multiply (10, 4, 3);
}

Output:

56
24
120

By declaring the function multiply with an argument list of (x, y, z), we made it so that when multiply is called, it must have three arguments supplied. The first time that main calls multiply, it calls it with (4, 7, 2). This tells the function multiply that x is now 4, y is now 7, and z is now 2. multiply can then calculate and display the product of these three numbers.

6.1.3 Functions - Enforced Arguments

Just as with local variables, functions in Hassium can take arguments that have an enforced type. For example, the function multiply from 6.1.2 might only want to take in ints, and raise an exception if a bool type was given. Using enforced arguments when possible can reduce runtime errors and make code more readable.

The syntax for an enforced parameter is <argName> : <type>. Let's rewrite the multiply function from 6.1.2 using enforced arguments.

func multiply (x : int, y : int, z : int) {
    int total = x * y * z;
    println (total);
}

This function will continue to work the same way as it did before, but if you supply a string or a float as one of the arguments, a runtime exception will be raised and the program will stop.

6.1.4 Functions - Variadic Arguments

Functions can accept what are known as variadic arguments (parameter type where a function can accept an unlimited amount of arguments). The variadic argument is prefixed with the identifier params and followed by the identifier of the argument. The arguments given in the function call go into a list which is given as the argument.

func main () {
    multiply (2, 3, 5, 4);        # prints 120
    multiply (4, 6);              # prints 24
    multiply (3, 3, [ 4, 5, 2 ]); # prints 360
}

func multiply (x, y, params nums) {
    total = x * y;
    foreach (num in nums) {
        total *= num;
    }
    println (total);
}

In main, the first two arguments given to the multiply function call go into the parameters x and y. After that, any arguments or a single list go into the parameter nums.

6.2 - Returning from Functions

A function can return (send a value back to the caller) a value. This is similar to how operators return values back to where they were called. To do this, the return statement must be used. The syntax for returning from a function is return <value>. Functions that have no explicit return will default to implicit return, returning the last evaluated expression from the stack.

The multiply function we worked with in 6.1.2 and 6.1.3 might wish to send the product back to the caller instead of displaying it to the screen, so that it could be used in another calculation.

func multiply (x : int, y : int, z : int) {
    int total = x * y * z;
    return total;
}

func main () {
    multiply (4, 8, 2);
    println (multiply (4, 2, 3));
    println (multiply (2, 7, 2) * 2);
}

Output:

24
48

Are you surprised there are only two lines of output? You shouldn't be. The very first call to multiply calculated the product of 4, 8, and 2. The value was returned, but nothing was done with that return value. The second line of main however uses the return value of multiply as the argument to println, displaying the product to the terminal. The third line multiplies the return value of multiply by 2, then displays that value to the terminal.

6.2.1 Returning - Enforced Returning

Just as with parameters and local variables, the return types of functions can be specified in the declaration to enforce a specific type to be returned from the function. This is good practice primarily for making the code more readable and the function easier to document, as well as ensuring that you are returning the correct value from your function.

The syntax for a function declaration with an enforced return type is func <name> (<argumentList>) : <returnType> <body>. The function multiply from 6.1.2, 6.1.3, and 6.2 returns an int. However, in order for someone reading your code to determine that multiply returns an int, they would have to either guess or look at your code in depth. We can save valuable time by explicitly declaring that multiply returns an int.

func multiply (x : int, y : int, z : int) : int {
    int total = x * y * z;
    return total;
}

The program should still work the exact same way it had worked before. To test the powers of enforced returning, modify the return statement in multiply to say return "something". This will raise a runtime exception when called, since the function is attempting to return a type that is not valid.

6.2 - Function Overloading

Hassium supports function overloading (process where two functions have the same name but a different argument list). Using this, you can define functions that have similar behaviors with the same name, but allow them to take different types of parameters.

Going back to the multiply function example from 6.1, let's say you wanted to have a function that would multiply three numbers and another function to multiply four numbers.

func main () {
    println (multiply (5, 3, 6));    # first version of multiply
    println (multiply (2, 7, 8, 3)); # second version of multiply
}

func multiply (x, y, z) : float {
    println ("First version of multiply was called!");
    return x * y * z;
}
func multiply (x, y, z, a) : float {
    println ("Second version of multiply was called!");
    return x * y * z * a;
}

Output:

First version of multiply was called!
90
Second version of multiply was called!
336

Because the first function call to multiply had three arguments given, the version of multiply that received three arguments was called. The second call gave four arguments, so the version of multiply that received four arguments was called.

Chapter 6 - Exercises

Exercise 6.1

Write and test a function called test that takes a string, int, and float as arguments. The function should display the string, then return a float that is the original float given divided by the int given. The return value to the function should be displayed to the terminal along with the message Returned: <value>.

Sample run Output:

test ("Hai", 7, 24.5);
Hai
Returned: 3.5

Exercise 6.2

Write and test functions that implement +, -, *, and / operators with taking in three numbers. You have already done this with multiply.

results matching ""

    No results matching ""