When designing systems, especially custom Luau Objects using OOP principles or multi-purpose functions, it can be very difficult to implement one function with multiple purposes or uses.
It can also be incredibly inefficient and bloaty to constantly check types and values of arguments to try and account for different types of values being passed.
This post suggests the ability to “overload” types and functions that are being redefined via the use of
local function +lerp<T>(a: T, b: T, c: number): T return a:Lerp(b, c) -- 'T' requires key/method 'Lerp' end --option 1 function +lerp<T:number>(a: T, b: T, c: number): T -- specification for when 'T' is 'number' return a + (b - a) * c end --option 2 function +lerp(a: number, b: number, c: number): number -- overload when a and b is 'number' return a + (b - a) * c end function +Quaternion.new(): Quaternion return Quaternion.identity end function +Quaternion.new(w: number, x: number, y: number, z: number): Quaternion -- build quaternion from components end function +Quaternion.new(axis: Vector3, angle: number): Quaternion -- build quaternion from axis angle end function +Quaternion.new(cf: CFrame): Quaternion -- build quaternion from cframe information end function +QuaternionClass.ToCFrame(self: Quaternion): CFrame -- generate CFrame from quaternion end function +QuaternionClass.ToCFrame(self: Quaternion, position: Vector3): CFrame -- generate CFrame from quaternion, with added position value end -- ToCFrame(self: Quaternion, position: Vector3?) but optimized when not using position argument -- if Quaternion supports :Lerp() lerp(quatA, quatB, 0.5) -- valid -- otherwise (i.e. it is named Slerp()) -- option 1 function +lerp<T:Quaternion>(a: T, b: T, c: number): T return a:Slerp(b, c) end -- option 2 function +lerp(a: Quaternion, b: Quaternion, c: number): Quaternion return a:Slerp(b, c) end
This will work will in tandem with a LuauDoc feature I also proposed, allowing users to document overloaded functions easily.
The good thing is that Luau already does support overloaded functions. All functions created in C/C++ for example have the potiential for overloading when binding it to a Lua function.
CFrame.new is a great example. Therefore, the implementation would be extend the function overloading ability to native Luau functions as well, which should now be possible due to Luau argument typing.
Some issues/questions may be apparent, but here is my suggestion on how to solve them:
Two overloads represent an ambiguous case
The ambiguous overloads should create a warning and the latest one will be used due to order of execution.
A function call is ambiguous due to loose types
Create a warning and call the first available overload unless the types are specified.
Functions get overloaded in another script via injected exploit
Add the ability to freeze a function to prevent further modification (like table.freeze), or make functions that are in frozen tables unable to be overloaded.
Functions that don’t have any overload should be frozen automatically.
There may also be the option to allow overloading only within the same ModuleScript or script/scope.
Changes in Function object’s behavior
Functions are already first-class objects due to Lua’s nature, and overloads would be additions to the object’s internal structure, similar to how a table’s contents can be modified yet the reference still holds when the table is resized.
Typing an overloaded function
Typing will just union all function prototypes of all overloads.
e.g. the quaternion.new() can represented as
()->Quaternion & (number,number,number,number)->Quaternion & (Vector3,number)->Quaternion & (CFrame)->Quaternion
Calling the function with the right parameters will automatically derive which overload to use.
Implementation does not have to match the mock-up, it is only there to demonstrate use cases