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
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.
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.
You are better off just asking for a better language at this point. LUAu will most likely never get first class support. But they will also not give you a better language since they could just as well make a new engine from scratch at that point which I hope they do.
People’s current weird methods of using types and m**atables is suboptimal. I’d love to see classes and constructors instead of having to use long types and tables.