Object-Oriented Programming

• When we wrote the driver program for our nonlinear solver module, we had to make extensive use of conditionals. In particular, we used the select case construct. For example, select case ( method name) ! selector is character string for name of method case (’newton’) ! use Newton’s method xkp1 = newton (xk, f at xk, fprime at xkp1) case (’secant’) ! use secant method xkp1 = secant(xkm1, xk, f at xk, f at xkm1 ) case default ! invalid method name input print *, "invalid method name input"; stop end select • We had to hardwire the call to each nonlinear solver and we had to code different statements to get ready for the next iteration as well as to read in starting values. • Wouldn’t it be nice if we could just have statements like call read in initial values ( ) xkp1 = next iterate ( ) call get ready for next iteration ( ) and the code would call the appropriate routine based upon the arguments used. • Object-oriented programming allows us to do this. • Objected-Oriented Programming (OOP for short) is becoming increasingly popular. • High level objected-oriented (OO) languages provide the user with three main capabilities that improve and simplify the design of codes. – encapsulation – inheritance – polymorphism (generic functionality)

• Related ideas include objects, classes and data hiding. • An OO language provides a way to couple or encapsulate the data and its functions into a unified entity. 90 uses modules and derived types for encapsulation which we have already used. • Inheritance in OOP has a similar meaning to the common usage of the word. Suppose we have defined a class in a module and then we want to write a second class which uses the first. Fortran allows us to inherit functionality from the first class into the second; i.e., we don’t have to reprogram it in the second unit. A nontechnical example of this would be if we defined a class of objects called employees (with pertinent information like SSN, DOB, etc) and then defined a second class called managers. Since managers are also employees we would like to “inherit” all the functionality of the employee class and then add the additional information for managers that is not needed for regular employees. We have seen a simple example of inheritance through the contains statement. • Polymorphism allows different classes of objects that share some common functionality to be used in code that requires only that common functional- ity. In other words, routines having the same generic name are interpreted differently depending on the class of objects presented as arguments to the routines. Some intrinsic functions have always had this capability. For exam- ple real (a) will convert an integer to a real if a is an integer whereas if a is complex, it gives the real part. Fortran 90 allows generic functions to also have this feature. A Simple Example to Illustrate Polymorphism and Inheritance

• Let’s return to the problem we first did to introduced derived types – a rect- angle and a circle class. • Our first goal is to write an OO code which does the following. – Recall that we defined two derived data types (i.e., data structures )- a rectangle and a circle. The definition of the data structure along with the supporting subprograms is called a class. – We wrote a function to calculate the area of a circle if its radius is given and another to calculate the area of a rectangle if its two sides are given. – We wrote a main or driver program which invoked either the function to calculate the area of the circle or the other function to calculate the area of the rectangle. We used conditionals to do this. Now we want to use a single statement to invoke either routine; , e.g., area = compute area(argument). If the argument is an object in the rectangle class then it calls the routine for calculating the area of a rectangle; if the argument is an object in the circle class it calls the routine for calculating the area of a circle. Remember that the two routines for calculating area of the object must have different names.

• If we can do this, then it will be an example of polymorphism.That is, the program behaviors differently (it calls different function routines) depending on what “type” the argument is. If we can do this, then it will be our first OO code. This is also called function overloading. • What new fortran commands do we need to accomplish our goal of coding this example of polymorphism?

– Interface statement - will allow us to call functions with different names by one generic name (function overloading); e.g., compute area could be the generic statment and it will call either compute area circle or compute area rectangle depending on the type (i.e., what class it be- longs to ) of the argument. Interface Statement for Function Overloading

– The interface statement will allow us to give a generic name to several routines. (It has other uses which we will see later.) – We must provide ∗ the generic name, and ∗ a list of all routines that can be called using this generic name. – Where do we put the interface statement? ∗ Probably the best place to put it is in the module before the contains although it can be put in the main program. ∗ This means that in our module for the rectangle we will have an interface statement for the routine compute area rectangle and another state- ment in the module for the circle for the routine compute area circle. Syntax for Interface Statement for Function Overloading

interface name

module procedure list of subprogram names end interface

For our example we could have for the module class rectangle interface compute area

module procedure compute area rectangle end interface • The main program uses both modules and each module procedure will be added to a list of procedures that have the generic name compute area. Ev- erytime you invoke compute area in the main program, the compiler checks the argument to see if the argument is an object in the circle class or in the rectangle class that you have defined (we will see how to do this in a minute) and calls the appropriate subprogram. • Of course the main program must know about the classes that we have defined in our modules but we already know how to do this via the use statement. The Module Revisited

The structure of a module with a data type definition

module name

implicit none

type statements interface statements

contains

subprograms (functions and ) related to defined class

end module name Sample module for defining a rectangle class module class rectangle use common data implicit none type rectangle ! definition of rectangle data structure real(prec) :: width, height end type rectangle interface compute area

module procedure compute area rectangle end interface contains function compute area rectangle ( rect ) result(area) type (rectangle) :: rect real(prec) :: area area = rect % width * rect % height end function compute area rectangle end module class rectangle • We first define the derived data type rectangle with two attributes which we declare. • We can call the function compute area rectangle by using its name. • However, because we included the interface construct we can also access it with the call compute area. • If we want to invoke compute area rectangle using compute area then it only works when the argument is a member of the rectangle data structure. Our interface construct is tied to the type definition. • In the function routine we had to define the object rect as a member of the rectangle derived data type. Sample module for defining a circle class module class circle use common data implicit none type circle ! defines circle data structure real(prec) :: radius end type circle interface compute area

module procedure compute area circle end interface contains function compute area circle ( circ ) result(area) type (circle) :: circ real(prec) :: area area = pi * (circ % radius)**2 end function compute area circle end module class circle Constructors and Destructors

• Other terminology which is used in OOP is that of constructors (which we have already used) and destructors for objects in a data structure. • Typically we write our own constructor/destructor for a class or use an intrinsic constructor. • The destructor is used to reinitialize the data structure definition or release storage when it is no longer needed. A Data Structure one of whose Components is itself a Data Structure

• When we define a data structure with the type construct we sometimes want to extend these attributes to define another data structure which has the attributes of the first plus some additional components. • A simple, non-scientific example of this is defining an employee data structure. • Suppose we want the attributes of the employee data type to contain – first and last name ( characters) – id number (an integer) – date of birth (month, day and year) – date started working (month, day and year) – current hourly wage (floating point) Note how these components are of different types. • Notice that two of the items are dates. So it makes sense to first define a date data structure. • Each date should contain the month (an integer from 1 to 12), a day of the month (an integer from 1 to 31), and a year (a 4 digit integer). So we might want to define a derived data type called date which has 3 integer components (month, day, year). Then each of the third and fourth components of our employee class is itself a class and we will declare it as such. • Examples of these two data structures are given below. module class employee use common data implicit none type (date) integer :: month, day, year end type date type (employee) character ( len = 30) :: first name character ( len = 30) :: last name integer :: id type(date) :: dos type(date) :: dob real(prec) :: hourly wage end type employee contains . end module class employee • Note that the way we declared the date of birth (dob) to be a member of the class date is the same syntax that we declared rect to be a member of the rectangle class in our main program geometry.f90. • We could define separate modules for these two classes or we could put them in the same module. An Example of a Numerical Calculation Class - Rational Numbers

• Recall that a rational number can be represented as the ratio of two integers. • In this example we will define a data structure for a rational number and add some subprograms related to performing operations on rational numbers (such as adding them, converting a rational number to a real number, etc.) • We will introduce the concept of public versus private. • We will look at another aspect of OOP called . • Because a rational number is determined by the integer numerator and integer denominator, we will define our class (data structure) for a rational number as

type rational integer :: num integer :: den end type rational • The goal is to add functionality to the module so that we can add or subtract two rational numbers, multiply two rational numbers, reduce a rational number to its simplest form, print out a rational number, convert it to a floating point number, etc. • The derived data type plus these subprograms and any interface statements will form our rational number class. Public and Private Accessibility

• By default all module variables and subprograms are available to all program units which have the use module name statement. • This may not always be desirable. If the module procedures (given in the interface construct) provide all the access that is necessary for users, it may be safer to not allow users the ability to interfere with the internal workings of your module. • By default, all names in a module are public but this can be changed with the private statement. • Since everything is public by default, you may choose to just declare the variables or routines that you want private; for example module class rational numbers implicit none private :: gcd, reduce Here everything is public except the subprograms reduce and gcd which are routines that are only called by other public subprograms in the module. The user would not even know of their existence unless he/she looked at the actual code. • If we have declared reduce as private and we have a call in the main program to this we will get the compile time error message undefined symbols : reduce just as if the program did not exist. • Alternately, you could declare everything as private and just list what you want to be public. For example, module class rational numbers implicit none private public :: add rational Here the entire routine is private and only the subprogram add rational is public. • For now we will mainly use the private specifier if our routine is only to be used by other subprograms in the module. Operator Overloading

• Suppose we write a subprogram to add two members of our class rational and output another member of our class. For the present, we will not worry about reducing our result to its simplest form; we can add this capability later. • Should we use a function or a subroutine? • Recall that to add two rational numbers we simply find a common denominator and combine, i.e., p r ps + rq + = q s qs • To define a new member of our class we need to specify its numerator and denominator. If this member is found as the sum of two other members of our class, we need to define its numerator and its denominator from the formula above. Consequently, our function could look like: function add two rationals (a, b ) result (c) type(rational) :: a, b type(rational) :: c c % num = a % num * b % den + b % num * a% den c % den = a % den * b % den end function add two rationals • Then to invoke this routine we could simply type rat = add two rationals ( a, b) where a, b, rat have been defined as members of our class rational. • However, wouldn’t it be easier if we could write rat = a + b • Operator overloading allows us to do this. • Note that we have already encountered operator overloading and we haven’t realized it. • For example, the usual operator+ responds differently when we write c= a + b if a, b, c are integers, reals, complex numbers, arrays, etc. • Operator overloading allows us to redefine this operator + based upon a data structure we have defined (such as our rationals). • To accomplish this each operator that you want to use has to be defined, or overloaded, for each data type. This is accomplished through the use of the interface operator construct and is somewhat similar to our example of function overloading when we used the generic name compute area to call compute area circle or compute area rectangle depending on the specific data type in the argument. • In our case we can use the following syntax to overload+ to use our function to add two rational numbers. It is only invoked when the objects we are adding are members of our defined data structure rational. As before, we will put it in our module that defines our data structure using this particular overloading. interface operator (+) module procedure add two rationals end interface • Of course operator overloading is not just restricted to +. • We can overload other operators such as -, *, /, >, <, ==. • In addition, we can overload an assignment operator =, . In this case we use the word assignment instead of operator. interface assignment (= ) module procedure set equal integer end interface • You can have more than one operator or assignment procedure but each one must take different argument types. For example, we can overload the oper- ator+ to use the routine add two rationals and also overload the same operator to add two vectors (which we will do soon). Then in the main program which uses both of these modules we can write c = a + b; then depending if a, b, c are vectors or rationals, the main program will call the appropriate routine. Our module now looks like this: module class rationals use common data implicit none private :: list routines that are not accessible to other users type rational

integer :: num integer :: den end type rational interface operator (+) module procedure add two rationals end interface contains our subprograms end module class rationals In the main program if we declare type(rational) :: a, b, c then when we have the statement c = a + b it will automatically call the function add two rationals with the first input as a, the second as b and the output in c. Summary

• Derived types are data structures which are the basis of OOP. A member of a derived type can also be a derived type. • Function overloading refers to calling different routines by a generic name (like compute area) and the routine called is determined by the data structure in the argument. The interface statement is used to associate the generic name with the actual name of the routine. • Operator overloading allows you to use operators such as +, −, ∗, /, <, >, == to invoke a routine instead of actually calling the routine. This is done by adding an interface operator statement to the module to associate the operator (such as +) being overloaded by the routine. • The private statement can be used in a module to block any program that has a USE statement for this module from having access to sections of the code. This should be used if one of the subprograms in the module calls another subprogram and this is the only way it is used.