I'm using metamethods for my "Number Instance" module to act like real numbers. Most of the metamethods are working fine, except for these three: __eq
, __le
, and __lt
. They are scripted the same way with __concat
, __add
, __sub
, __mul
, __div
, __mod
, and __pow
, but they won't work properly as expected. I tried printing them in a script and this is what they printed:
local NumberService = require(game:GetService("ReplicatedStorage"):WaitForChild("CustomServices")):GetService("NumberService") local TestNumber = NumberService.new(50) print("My number is " .. TestNumber) -- "My number is 50" (working) __concat print(-TestNumber) -- -50 (working) __unm print(TestNumber + 50) -- 100 (working) __add print(60 - TestNumber) -- 10 (working) __sub print(TestNumber * 2) -- 100 (working) __mul print(50 / TestNumber) -- 1 (working) __div print(TestNumber % 28) -- 22 (working) __mod print(2 ^ TestNumber) -- 1125899906842624 (working) __pow print(tostring(TestNumber)) -- "50" (working) __tostring print(50 == TestNumber) -- false (not working) __eq print(TestNumber < 60) -- attempt to compare table < number (not working) __lt print(TestNumber > 60) -- attempt to compare table > number (not working) __lt print(40 <= TestNumber) -- attempt to compare table <= number (not working) __le print(TestNumber >= 40) -- attempt to compare table >= number (not working) __lt print(#TestNumber) -- attempt to get length of a number value (working) __len
This is the expected output to the lines that are not working
print(50 == TestNumber) -- true print(TestNumber < 60) -- true print(TestNumber > 60) -- false print(40 <= TestNumber) -- true print(TestNumber >= 50) -- true
And this is the module script
newNumber.__index = NumberInstance newNumber.__concat = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number .. b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number .. b elseif not isNumberInstance1 and isNumberInstance2 then return a .. b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a .. b end end newNumber.__unm = function(self) return -(self.Number) end newNumber.__add = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number + b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number + b elseif not isNumberInstance1 and isNumberInstance2 then return a + b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a + b end end newNumber.__sub = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number - b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number - b elseif not isNumberInstance1 and isNumberInstance2 then return a - b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a - b end end newNumber.__mul = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number * b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number * b elseif not isNumberInstance1 and isNumberInstance2 then return a * b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a * b end end newNumber.__div = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number / b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number / b elseif not isNumberInstance1 and isNumberInstance2 then return a / b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a / b end end newNumber.__mod = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number % b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number % b elseif not isNumberInstance1 and isNumberInstance2 then return a % b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a % b end end newNumber.__pow = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number ^ b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number ^ b elseif not isNumberInstance1 and isNumberInstance2 then return a ^ b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a ^ b end end newNumber.__tostring = function(self) return tostring(self.Number) end newNumber.__eq = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number == b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number == b elseif not isNumberInstance1 and isNumberInstance2 then return a == b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a == b end end newNumber.__lt = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number < b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number < b elseif not isNumberInstance1 and isNumberInstance2 then return a < b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a < b end end newNumber.__le = function(a, b) local isNumberInstance1 = typeof(a):lower() == "table" and a.Number ~= nil local isNumberInstance2 = typeof(b):lower() == "table" and b.Number ~= nil if isNumberInstance1 and isNumberInstance2 then return a.Number <= b.Number elseif isNumberInstance1 and not isNumberInstance2 then return a.Number <= b elseif not isNumberInstance1 and isNumberInstance2 then return a <= b.Number elseif not isNumberInstance1 and not isNumberInstance2 then return a <= b end end newNumber.__len = function(self) error("attempt to get length of a number value", 0) end
It turns out __eq
, _lt
, and __le
doesn't fire if the other token is a different datatype from the class.
For example:
MyCustomClass == "30"
Let's say you have a backend code that helps you to return the value MyCustomClass.String == "30"
, it will still not fire because MyCustomClass does not share the same datatype with "30". This rule is shared with the equal, bigger, or less than the comparative operator.
Also, a workaround is to do NumberService.new(50) == TestNumber
in your case, so 50 shares the same datatype with TestNumber.