local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
...
elseif (type(b) == "number") then
-- a is a vector, b is a scalar
local vector, scalar = a, b
return vector2.new(vector.x / scalar, vector.y / scalar)
elseif (a.__type and a.__type == "vector2" and b.__type and b.__type == "vector2") then
-- both a and b are vectors
return vector2.new(a.x / b.x, a.y / b.y)
...
Notice the type comparison between two vector2’s uses a __type table index
After
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
...
function mt.__typeof(t)
return t.__type
end
...
elseif (type(b) == "number") then
-- a is a vector, b is a scalar
local vector, scalar = a, b
return vector2.new(vector.x / scalar, vector.y / scalar)
elseif (typeof(a) == "vector2" and typeof(b) == "vector2") then
-- both a and b are vectors
return vector2.new(a.x / b.x, a.y / b.y)
...
Possibly confusing since one would always be expecting the typeof to return the internal Roblox type. It seems in your specific case it may be easier to implement a :typeof() method to the classes rather than overloading an already present function that you are expecting a specific use from.
I don’t think this is necessary. Currently “typeof()” is only 30% slower than “type()” for Lua types, and I’d prefer to keep it that way. Adding new methods would bloat the global environment.
If you need this behavior you can implement it in less than 10 lines:
local function customTypeof(v)
local t = typeof(v)
if t == "table" then
local mt = getmetatable(v)
return type(mt) == "table" and rawget(mt, "__typeof") or t
end
return t
end
local object = setmetatable({}, {__typeof = "CustomTypeExample"})
print("Test:", customTypeof(object))
typeof/type speed is < 1ms up to 10,000 calls. Even on low-end devices, it would take an insane number of calls for any speed difference to be noticeable. If you’re calling typeof so often that type-checking is your bottleneck, you’re doing something very wrong. This seems like premature optimization and not a realistic problem.
There are multiple issues with this check to begin with which makes it a rare case, and it seems what you’re doing is just general bad practice. From looking at this, what I can infer is:
You are passing/having a variable “class” which may be of multiple types despite the name class suggesting it’s either an userdata or table implementation. Essentially, having a variable which can have an arbitrary value of a different type at any given time.
Your classes do not share common inheritance to a base class to provide the methods such as :typeof (such as Roblox does with the Instance class functionality).
Adding to typeof() global may bloat it more than what it is for already, which is again expected to return a Roblox-defined type, and could potentially be used for spoofing internal functionality depending on how it’s implemented (calling a method that expects something with a __type overriden userdata.
It’s important to type-check everything received via RemoteEvents on the server. Right now I get about 17,000/ms for Lua types and 8000/ms for Roblox types.
I make my argument not because typeof() is a bottleneck for me currently, but because it could become a bottleneck for future use-cases if features like these are added.
I find myself wanting the same thing, and I figure I would add in my use case instead of starting a new thread. I agree that this would be a handy addition, particularly to validate the inputs of a function (defensive programming). For example, I am currently writing:
function class:method(arg)
if typeof(arg) ~= "table" or arg.isA == nil or not arg:isA("Matrix") then
error("arg must be a Matrix : Actual " .. typeof(arg))
end
--do method
end
There are a few things to note here: I need to first use typeof(arg) to check that the argument has the potential to be an instance of something, I then have to check that the object contains the function that I defined to do the type/instance check (I call it isA to reflect Instance:IsA, the name isn’t too important), and then I can actually do the check that I am wanting to do. In the event that the check fails, I would then get an error saying arg must be a Matrix : Actual table
Having a __typeof
I find myself wanting the same thing, and I figure I would add in my use case here instead of starting a new thread. I agree that this would be a handy addition, particularly to validate the inputs of a function (defensive programming stuff). For example, I am currently writing:
function class:method(arg)
if typeof(arg) ~= "table" or arg.isA == nil or not arg:isA("Matrix") then
error("arg must be a Matrix : Actual " .. typeof(arg))
end
--do method
end
There are a few things to note here: I need to first use typeof(arg) to check that the argument has the potential to be an instance of something, I then have to check that the object contains the function that I defined to do the type/instance check (I call it isA to reflect Instance:IsA, the name isn’t too important), and then I can actually do the check that I am wanting to do. In the event that the check fails, I would then get an error saying arg must be a Matrix : Actual table
Having a __typeof metamethod (could be named anything really) would do a few things:
Make the code much cleaner and clearer
function class:method(arg)
if typeof(arg) ~= "Matrix" then
error("arg must be a Matrix : Actual " .. typeof(arg))
end
--do method
end
Make validation consistent with Roblox types like Vector2
Add extra information to the error message (say for a made up user-defined object) arg must be a Matrix : Actual Vector4
Adding a field to Vector4 to print out during an error doesn’t necessarily help my case here, since I would have to do an additional check to ensure that the passed argument has that variable (after all, I have no idea what it is). If I was given a Vector2 or nil, arg._typeToPrint would cause an error in and of itself. Just as an illustration of how messy this becomes,
if typeof(arg) ~= "table" or arg.isA == nil or not arg:isA("Matrix") then
if typeof(arg) == "table" and arg._typeToPrint ~= nil then
error("arg must be a Matrix : Actual " .. arg._typeToPrint)
else
error("arg must be a Matrix : Actual " .. typeof(arg))
end
end
I partially support this idea although I don’t like the idea of it overriding typeof specifically. The reason for this is it would make sandboxing scripts much harder not to mention malicious scripts can pretend to return types that they aren’t. I think if this came in the form of typeof(value, unsafe) I would be much happier about this. This doesn’t add too much to type out and fixes this issue.
Secondly, I think if this supported a function callback this could get way more use cases. For example, let’s say you have something like this:
local types = {}
local MyClass = {}
setmetatable(types, {
__index = function(self, index)
if index == MyClass then
return "MyClass"
end
return "Unknown"
end
})
function MyClass:ConvertTo(Class)
types[self] = typeof(Class, true).."()" -- Now it's an instance!
Class:ConvertFrom(self)
end
setmetatable(MyClass, {
__typeof = function(self)
return types[self]
end
})