Classes

This tutorial shows how you can implement user-defined classes with private fields and public functions into your scripts.

Introduction

In this tutorial we will have a look at how classes are implemented in HaasScript and how you can use them.

Classes are useful in cases where you need to have multiple instances with same functionality. Examples of these instances would be a custom order management system and a bot system for easier multi-market management.

Creating new script

First off, we will start by creating a new script in the script editor.‌

Keep the script type as default (Script) and name it "Class Tutorial".

The class is a function

Classes in HaasScript are created inside a function as objects and new instances of these objects are returned when calling that function.

Here we start off by creating a function called Calculator, and we give it 2 input parameters.

local Calculator = function(input1, input2)
    -- Code here
end

There's our starting point.

Objects

Objects in HaasScript are as in any programming language; collections of accessible data fields and/or functions. Defining an object happens as follows:

local object = {}

Oh, what is that you say? It's an array? Pffft. I beg to differ...

local Person = {
    Name = "Some Old Guy",
    Age = 50,
    SayHello = function(name)
        return name..' says hello!'
    end
}

Log(Person.SayHello( Person.Name )) -- Outputs 'Some Old Guy says hello!'

Although this isn't exactly what we are looking for; this object is static - we can't create multiple instances of this object and we can't access the fields within the object itself:

local Person = {
    Name = "Some Old Guy",
    Age = 50,
    SayHello = function()
        return Name..' says hello!' -- Trying to use field 'Name'
    end
}

Log(Person.SayHello()) -- ERROR: Unknown references: Name

Function to return objects

Thankfully there's a solution for this: function to return objects. This way, whenever we call the function, we will get a new instance of that object and the things inside will also work a little different... Maybe?

Let's continue by giving our Calculator two fields and a function.

local Calculator = function(input1, input2)
    local value1 = input1
    local value2 = input2
    
    local calc = function()
        return value1 + value2
    end
end

Great! But... Wait a minute... How do we access these fields and function? This function doesn't return anything!?

Right, of course. We create an object out of them by simply assigning the insides of the function to its new handlers:

local Calculator = function(input1, input2)
    local value1 = input1
    local value2 = input2
    
    local calc = function()
        return value1 + value2
    end
    
    -- Return object
    return {
        Calculate = calc,
        Value1 = value1,
        Value2 = value2
    }
end

There we go. There's one thing I have to mention though...

Getters and Setters

We need these - BAD.

The Value1 and Value2 are visible outside of our class, sure. But if we change the values, they wont change internally.

Let me show you what I mean...

local myCalc = Calculator(1, 2)

Log( myCalc.Value1 ) -- Outputs 1, since input1 got the number 1
Log( myCalc.Value2 ) -- Outputs 2, since input2 got the number 2

Log( myCalc.Calculate() ) -- Outputs 3

-- Change the values
myCalc.Value1 = 10
mycalc.Value2 = 20

Log( myCalc.Value1 ) -- Outputs 10
Log( myCalc.Value2 ) -- Outputs 20

Log( myCalc.Calculate() ) -- Outputs 3, since internal values are still 1 and 2

Isn't it weird? Yes and no I'd say. The exposed fields that we change the value for are their own variables and they do not access the internal fields in any way. They do get their values from the internal value1 and value2 but only when the object is created. After that, they are what they are. Same would happen if we changed the Calculate function to something else.

You could consider these as some type of "memory slots" that are specific to the created object.

I know it sounds confusing, but don't worry. I'll show you what we can do to grant us access to the insides:

local Calculator = function(input1, input2)
    local value1 = input1
    local value2 = input2

    -- Add functions to manipulate value1 and value2
    local getVal1 = function() return value1 end
    local setVal1 = function(newVal) value1 = newVal end
    local getVal2 = function() return value2 end
    local setVal2 = function(newVal) value2 = newVal end

    local calc = function()
        return value1 + value2
    end
    
    -- Return object
    return {
        Calculate = calc,

        -- We wont be needing these anymore...
        --Value1 = value1,
        --Value2 = value2,
        
        -- Remember to include our new functions here!
        GetValue1 = getVal1,
        SetValue1 = setVal1,
        
        GetValue2 = getVal2,
        SetValue2 = setVal2
    }
end

There we go. Now we have 4 functions that actually "go inside" our class and does something in there. This way, we can manipulate the values and it will affect the outcome of our Calculate method.

So let's try that earlier experiment again, this time with our getters and setters:

local myCalc = Calculator(1, 2)

Log( myCalc.GetValue1() ) -- Outputs 1, since input1 got the number 1
Log( myCalc.GetValue2() ) -- Outputs 2, since input2 got the number 2
Log( myCalc.Calculate() ) -- Outputs 3, okay...

-- Change the values
myCalc.SetValue1( 10 )
mycalc.SetValue2( 20 )

Log( myCalc.GetValue1() ) -- Outputs 10
Log( myCalc.GetValue2() ) -- Outputs 20
Log( myCalc.Calculate() ) -- Outputs 30, WOOP!

Final words

That's pretty much it for classes. You know what to do next...

Last updated