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 type
or function
keywords.
Mock-up example:
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