Tutorial: Inheritance in Quorum
Quorum uses inheritance for establishing relationships between classes.On Inheritance
We start our exploration of inheritance, a method for reusing code commonly seen in many programming languages, with an example on inheriting from Quorum’s math class. Imagine, for example, there were certain mathematical operators that we wanted to use. One way we could access the actions in Math would be to instantiate a Math object and use it. Another would be to inherit from it. There are good reasons we might want to do either one, but first, we examine what we type:
use Libraries.Compute.Math
class Main is Math
action Main
output "I am inheriting from Math!"
end
end
Notice that we accomplish this by first putting in a use statement for the Math class, indicating where it lives. Second, after the definition of our class, Main, we use the keyword is and then the name of the class we want to inherit from. Next, we define our Main action normally and, just so something appears in our console for this example, an output statement.
Now that we have inherited from the Math class, we can access all of its actions. Here is a list of the actions Math provides by default:
Quorum's Math ClassSince we have inherited from Math we can call one of the Math actions even though we did not define them. For example, the following example returns 4.0:
use Libraries.Compute.Math
class Main is Math
action Main
number value = RaiseToPower(2, 2)
output value
end
end
The key here is to realize that the RaiseToPower action, the parameters here making its meaning 2-squared, can be called because the Math class has such an action. By inheriting, we are asking the Math class for permission to use it. Because this action is public, Math gives permission.
Overriding Actions
In addition to using actions from the Math class, we can also add our own and even change the meaning of some of the parent actions. This is a common thing to do. If we are using the Game class to write games in Quorum, we might override the Update action in order to change what happens on each frame of animation in our game. Or, if we were overriding the Array class, we might change the way arrays are sorted if we have a faster or better approach. Here is an example of inheriting and calling an action from the Random class:
use Libraries.Compute.Random
class Main is Random
action Main
integer value = RandomInteger()
output value
end
end
By default, the RandomInteger action returns a number between 0 and the maximum integer on the system, which is 2,147,483,646. Suppose, however, that we needed this action to return a different maximum number. Normally, this would not be desirable, as people using our class like the Random class might not expect it. Point being: inheritance involves design and there are good ones and bad ones. Being able to spot the difference, in this case a relatively weak design, takes practice and iteration. Here is our example, now with action overriding:
use Libraries.Compute.Random
class Main is Random
action Main
integer value = RandomInteger()
output value
end
action RandomInteger returns integer
return RandomInteger(1000)
end
end
In this case, the RandomInteger returns integer action now provides numbers between 0 and 1000. Even though we have inherited from Random, users of our class will receive our functionality, not that of the parent class.
Inheriting from Multiple Classes
Programming languages differ significantly in how inheritance works. Some programming languages, like Java, have different styles of inheritance as compared to C++ or other languages. Unfortunately, how this impacts programmers in practice is poorly understood in the academic literature (e.g., which systems are easier to use, by whom, and under what replicable conditions?). Quorum’s system is called multiple inheritance, although it differs significantly from well-known systems of multiple inheritance in languages like C++.
If we wanted to inherit from two classes in Quorum, we create a comma separated list of the names in the class definition, like so:
use Libraries.Compute.Random
use Libraries.Compute.Math
class Main is Math, Random
action Main
integer value = RandomInteger()
output value
value = AbsoluteValue(-5)
output value
end
end
In this case, notice that we can now call actions from the Random class, like RandomInteger, and from the Math class, like AbsoluteValue. This process works in a similar way conceptually to how many programming languages, like Java, C#, or C++, use inheritance. The difference is in how we access variables from the parents (Math and Random).
Accessing Parent Variables
The Math class in Quorum has variables inside of it, including some common constants like pi. To access them, we need to tell Quorum where the variable comes from. So, if we wanted to output the value of pi to the screen, we could do it like so:
use Libraries.Compute.Random
use Libraries.Compute.Math
class Main is Math, Random
action Main
number pi = parent:Math:pi
output pi
end
end
The crucial point here is that we first use a special keyword, called parent, and then provide which class we want to obtain a variable from. In this case, that is Math. Then, we say which variable we would like to obtain, in this case pi, and then return and output it. This process works the same regardless of the parent and the type. Only public variables from the parent can be accessed.
Inheritance can be a complicated topic and students in college level courses may spend a few weeks or more going over the various details, depending on the language and its quirks. Even once the mechanics are understood, there are many ways in which we can use inheritance for advanced designs (e.g., design patterns). While the tutorial here can get us started using the feature in Quorum, it can be used for far more advanced functionality than hinted at here and is an important part of the game engine.
Next Tutorial
In the next tutorial, we will discuss generics, which describes how to use this concept for collections of data.