In this section, we will deep dive into Swift function and method syntax. Boring stuff alert! To make it a little more interesting, revisit or remember the function definition in mathematics and compare the functions and methods you write to math functions.
If you think that you already know the details or if it is not that interesting for you now, you can fast read or skip this first section, and go to the Return values from functions section of this chapter as it is directly related to FP.
Let's get it over with! We define functions or methods as the following:
accessControl methodForm func functionName(parameter: ParameterType) throws -> ReturnType { }
As we know already, when functions are defined in objects they become methods.
The first step to define a method is to tell the compiler where it can be accessed. This concept is called access control in Swift and there are five levels of access control. We are going to explain them for methods as follows:
- open and public access: Any entity can access a method that is defined as public if it is in the same module. If the entity is not in the same module, we will need to import the module to be able to call the method. We need to mark our methods as open/public when we develop frameworks in order to enable other modules to use them. open access level enables subclasses to override the methods while public access level restricts the ability to override.
- internal access: Any method that is defined internal can be accessed from other entities in a module but cannot be accessed from other modules.
- fileprivate access: Any method that is defined fileprivate can be accessed only from the defining source file.
- private access: Any method that is defined private can be accessed only from its enclosing declaration.
By default, if we do not provide the access modifier, a variable or method becomes internal. Using these access modifiers, we can structure our code properly; for instance, we can hide details from other modules if we define an entity as internal. We can even hide the details of a method from other files if we define them as private.
Before Swift 2.0, we had to define everything as public or add all source files to the testing target. Swift 2.0 introduced the @testable import syntax that enables us to define internal or private methods that can be accessed from testing modules.
The second step to method definition is to declare the method form. In Swift, methods can be generally in three forms:
- Instance methods: We need to obtain an instance of an object (in this book we will refer to classes, structs and enums as objects) to be able to call the method defined in it; then we will be able to access the scope and data of the object.
- Static methods: Swift names these type methods also. They do not need any instances of objects and they cannot access the instance data. They are called by putting a dot after the name of the object type (for example, Person.sayHi()). Static methods cannot be overridden by the subclasses of the object they reside in.
- Class methods: Class methods are like static methods but they can be overridden by subclasses.
We have covered the keywords that are required for method definitions; now we will concentrate on the syntax that is shared among functions and methods. There are other concepts related to methods that are out of the scope of this book because we are concentrating on functional programming in Swift.
Continuing to cover the function definition, now comes the func keyword that is mandatory and is used to tell the compiler that it is going to deal with a function.
Then comes the functionName that is mandatory and is recommended to be camel-cased with the first letter as lowercase. The function name should be stating what the function does and is recommended to be in the form of a verb when we define our methods in objects.
Basically, our classes will be named nouns, and methods will be verbs that are in the form of orders to the class. In pure functional programming, as the function does not reside in other objects, they can be named by their functionalities.
Parameters follow the func name. They will be defined in parentheses to pass arguments to the function. Parentheses are mandatory even if we do not have any parameters. We will cover all aspects of parameters in the Defining and using function parameters section of this chapter.
Then comes throws, which is not mandatory. A function or method that is marked with the throws keyword may or may not throw errors. We will cover error handling mechanisms in upcoming chapters. At this point, it is enough to know what they are when we see them in a function or method signature.
The next entity in a function type declaration is the return type. If a function is not Void, the return type will come after the -> sign. The return type indicates the type of entity that is going to be returned from a function.
We will cover return types in detail in the Returning values from functions section of this chapter, so now we can move on to the last piece of the function that is present in most programming languages, our beloved { }. We defined functions as blocks of functionality and { } defines the borders of the block so that the function body is declared and execution happens within them. We will write the functionality inside { }.