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
.