Object oriented progamming: basic concepts

By Samir Tine, published on January 2023

Objects in Luart

An object in Luart is a type in its own right, similar to standard data types (strings, numbers, booleans...). The concept of an object can be described as a pattern that defines specific capabilities of data. As an example, a "Fruct" object is defined by :

  • Attributes : color, shape...
  • Behaviors : peeled, eat...

By doing this, it will be possible to represent any fruit. This is exactly what object programming is all about: defining entities by their behaviors and attributes so that you can then easily create new data values without the need to rewrite code. Here's how to create a Fruct object with Luart :

Fruct = Object { } -- prints "Object" because Fruct is an Object print(type(Fruct))

Object attributes

The attributes of an object can, as we have seen, define the characteristics of an object. Let's define attributes for the "Fruct" object:

Fruct.color = "" Fruct.shape = ""

Another way is to provide the attributes directly when defining the object:

Fruct = Object { color = "", shape = "" }

Object instances

An object is therefore a rather conceptual thing that describes how "a value" of this object should behave. "A value" of this object is called an "instance", in real life. In our example, a Fruct object is a concept that represent a lot of possible instances in real life : apples, bananas, peers... In this case, an apple is called an "instance" of a Fruct. Let's create our first Fruct instance :

apple = Fruct() print(type(apple))

That's it, we have created a new kind of Fruct ! We can see that the apple has a type of "Fruct", and that it has been created by calling the Fruct object like a function. An instance is a living value of an object that has been created in memory. Usually an instance will have values assigned to it's attributes that differentiates it from other instances of the same type of object. Now let's customize the attributes of the apple instance :

apple.color = "red" appel.shape = "spherical"

We can now create another Fruct instance :

banana.color = "yellow" banana.shape = "curved"

Object methods

We have seen that objects are defined by their attributes and abilities. By abilities we actually mean the ability to perform an action, a behavior. This is done simply by defining object functions (also called methods). Let's add a "describe" behaviour to our Fruct object :

function Fruct:describe() print("I am a "..self.color.." and "..self.shape.." fruct") end

Object methods are defined like table methods with the colon ":" notation (which provides the implicit self function parameter) Let see if we can describe our apple and banana instances :

apple:describe() -- prints "I am a red and spherical fruct" banana:describe() -- prints "I am a yellow and curved fruct"

That's it. You just have defined an object behaviour that can be used in instances. We can even use this behaviour with the Fruct object, but it's not really functionnal, as an object is not intended to be used (remember, it's just a pattern that describes the capabilities of its instances) :

Fruct:describe() -- prints "I am a and fruct"

It's not attractive, let's try to improve this message from the Fruct object :

function Fruct:describe() if type(self) ~= "Object" then -- self is a Fruct instance print("I am a "..self.color.." and "..self.shape.." fruct") else -- self is the Fruct object print("I am a conceptual Fruct") end end

Here we evaluate the type of self to propose a different message depending on whether the describe() method was called by the Fruct object or by one of its instances. A more attractive way is to use the global is() function rather than evaluating the type of self :

function Fruct:describe() if is(self, Fruct) then -- self is a Fruct instance print("I am a "..self.color.." and "..self.shape.." fruct") else -- self is the Fruct object print("I am a conceptual Fruct") end end

The is() function checks if a value is an instance of an Object. Let's try it on our apple and banana values :

is(apple, Fruct) -- true is(banana, Fruct) -- true is(apple, banana) -- false

You know the basics about objects and instances in Luart now. The next part of the object oriented programming tutorial will cover object constructors and properties.