Object-oriented programming in luau

As a Roblox developer, it is currently too hard to use object-oriented programming. Unfortunately, currently there is no full-fledged OOP support in luau, but only its imitation with the help of metatables. My idea is to add the Class operator, which will be used to create classes.

Example:

Class Car
   name: string

   constructor(name: string)
      self.name = name
   end

   printName()
      print(self.name)
   end
end

local newCar = new Car("Car")
newCar:printName() -- Car

--inheritance:

Class SuperCar <- Car
   speed: number

   constructor(name: string, speed: number)
      super(name)
      self.speed = speed
   end

   printSpeed()
      print(self.speed)
   end
end

local newSuperCar = new SuperCar("Car2", 50)
newSuperCar:printSpeed() -- 50
newSuperCar:printName() -- Car2

If Roblox is able to address this issue, it would improve my development experience because using the current OOP in lua, typing problems appear, as well as the code looks less readable and beautiful. I’ve had experience with OOP in typescript, and I know what I’m talking about

1 Like

Types exist, which act just as classes to my knowledge, I might be wrong though

Types aren’t real code, it just symbolizes the structure of code.

java script

that’s all I’m gonna say

class Car {
    static print {
        console.log("hi");
    }
    constructor(carType, owner) {
         this.CarType = carType;
         this.Owner = owner;
     }
}

const Ford = new Car("Ford F150", "Doomcolp");
const Corolla = new Car("Corolla", "Doomcolp");

sorry I forgot how to use the super keyword

It is not that hard to do OOP in Luau, and no one needs first-class support for it.

If anything, you should ask for type-checking improvements instead.

2 Likes

Well private, and public variables would be nice. Something even better would be interface/abstract classes.

The official book for Lua (“Programming in Lua, Fourth edition”) offers some insight into why there is no distinction between public and private members:

[An object in Lua] does not offer privacy mechanisms. Partly, this is a consequence of our use of a general structure (tables) to represent objects. Moreover, Lua avoids redundancy and artificial restrictions. If you do not want to access something that lives inside an object, just do not do it. A common practice is to mark all private names with an underscore at the end. You immediately feel the smell when you see a marked name being used in public.

Simply put, Lua is designed to be flexible and allows the programmer to use the language how they see fit. Luau builds off this principle by offering optional type assignment and type-checking mechanisms (especially under --!strict mode) which can be leveraged to create what other languages would call an interface or abstract class.

Example 'Interface' in Luau

Create a Script with a child ModuleScript for this example.

--!strict
--[[ This is in the Script ]]
type Interface = { --This type could be assigned elsewhere in the codebase
   Func1: (number) -> boolean
   Func2: (number, number) -> boolean
}

local myInterface: Interface = require(script.ModuleScript)

Unless the child ModuleScript has members Func1 and Func2, the line assigning myInterface will be marked with a warning indicating the assigned value is incompatible with the type Interface. Additionally, if --!strict mode is also enabled in the ModuleScript, the warning will persist unless the members Func1 and Func2 have matching function definitions (matching argument types and return types). Below is an example of compatible contents for the ModuleScript to remove the warnings.

--!strict
--[[ This is in the ModuleScript ]]
local module = {}
function module.Func1(x: number): boolean
   return true
end
function module.Func2(x: number, y: number): boolean
   return true
end
return module

The code would still be allowed to “compile” despite the presence of warnings under --!strict mode and would only error when an operation actually causes an error (but not due to type conflicts). Again, Lua and Luau are based on the principle of allowing the programmer to write code with the flexibility to be as strict as they want or to “break the rules” when they want.

The Luau website also provides an example on Adding types for faux object oriented programs (luau-lang.org).

1 Like

Technically, the RFC on records on the luau github.

Lua does not have first class support for objects since all neccessary behaviour that objects inherit can be done with tables and metatables, and Lua’s base syntax is meant to be lightweight.

OOP in Lua is more akin to defining struct objects with member functions rather than objects, but it can still behave like OOP.

More specifically in Luau, a lot of people use the __index metamethod method as this is just a common pattern in Lua. This is because Lua always created new closures (internal function thing) when you created an object.

In Luau though, if you define your objects correctly, you dont need this pattern (Catwork only uses metatables to override tostring behaviour), because Luau will reference an existing closure under certain circumstances, instead of creating a new one, though thats too technical to explain here.

There are some micro-ops with having less keys on tables but its so minimal it doesn’t matter, key hashes are tiny compared to closures. It also makes iteration less predictable if the methods are not there.