Metatable compare problem

I want to achieve a complete vec2 (Vector2) class using Metatables.
However, there’s a somewhat confusing problem: “attempt to compare table”


Here’s the script that creates the class values:

local vec2 = require(script.Parent.vec2)
-- Print either true or false, but I'm getting errors instead.
print(tostring(vec2(5, 21) < vec2(7, 26)))

image
I’m just keeping things simple for me right now, as this is the very first time I learned how to tie a knot with metatables. I’m just really excited, because somehow I just suddenly understood how to use metatables, after weeks of constant confused confusing confusion!


Here’s the error I’m getting while trying to do any compare methods (==, <=, <):
image
attempt to compare table


I haven’t found a single solution around the internet, this might be really stupid but I don’t understand why this won’t work.


And finally, here's the class module:
local function new(x, y)
	return setmetatable(
		{
			__type = "vec2",
			x = x or 0,
			y = y or 0
		},
		{ -- Functions
			__index = self,
			__newindex = function(self, i, v)
				return rawset(self, i, v)
			end,
			__add = function(a, b)
				if (typeof(a) == "number") then
					return new(a + b.x, a + b.y)
				elseif (typeof(b) == "number") then
					return new(a.x + b, a.y + b)
				else
					return new(a.x + b.x, a.y + b.y)
				end
			end,
			__sub = function(a, b)
				if (typeof(a) == "number") then
					return new(a - b.x, a - b.y)
				elseif (typeof(b) == "number") then
					return new(a.x - b, a.y - b)
				else
					return new(a.x - b.x, a.y - b.y)
				end
			end,
			__mul = function(a, b)
				if (typeof(a) == "number") then
					return new(a * b.x, a * b.y)
				elseif (typeof(b) == "number") then
					return new(a.x * b, a.y * b)
				else
					return new(a.x * b.x, a.y * b.y)
				end
			end,
			__div = function(a, b)
				if (typeof(a) == "number") then
					return new(a / b.x, a / b.y)
				elseif (typeof(b) == "number") then
					return new(a.x / b, a.y / b)
				else
					return new(a.x / b.x, a.y / b.y)
				end
			end,
			__mod = function(a, b)
				if (typeof(a) == "number") then
					return new(a % b.x, a % b.y)
				elseif (typeof(b) == "number") then
					return new(a.x % b, a.y % b)
				else
					return new(a.x % b.x, a.y % b.y)
				end
			end,
			__pow = function(a, b)
				if (typeof(a) == "number") then
					return new(a ^ b.x, a ^ b.y)
				elseif (typeof(b) == "number") then
					return new(a.x ^ b, a.y ^ b)
				else
					return new(a.x ^ b.x, a.y ^ b.y)
				end
			end,
			__eq = function(a, b)
				if (typeof(a) == "number") then
					return (a == b.x) and (a == b.y)
				elseif (typeof(b) == "number") then
					return (a.x == b) and (a.y == b)
				else
					return (a.x == b.x) and (a.y == b.y)
				end
			end,
			__lt = function(a, b)
				if (typeof(a) == "number") then
					return (a < b.x) and (a < b.y)
				elseif (typeof(b) == "number") then
					return (a.x < b) and (a.y < b)
				else
					return (a.x < b.x) and (a.y < b.y)
				end
			end,
			__le = function(a, b)
				if (typeof(a) == "number") then
					return (a <= b.x) and (a <= b.y)
				elseif (typeof(b) == "number") then
					return (a.x <= b) and (a.y <= b)
				else
					return (a.x <= b.x) and (a.y <= b.y)
				end
			end,
			__tostring = function(a)
				return a.x .. " " .. a.y
			end,
			__unm = function(a)
				return new(-a.x, -a.y)
			end
		}
	)
end

return new

The arithmetic methods work just fine, the logical methods don’t.

Consider moving the metatable out of the function. Your current code creates a new metatable every time new is called.

local new
local mt = { -- Functions
	__newindex = function(self, i, v)
		return rawset(self, i, v)
	end,
	__add = function(a, b)
		if (typeof(a) == "number") then
			return new(a + b.x, a + b.y)
		elseif (typeof(b) == "number") then
			return new(a.x + b, a.y + b)
		else
			return new(a.x + b.x, a.y + b.y)
		end
	end,
	__sub = function(a, b)
		if (typeof(a) == "number") then
			return new(a - b.x, a - b.y)
		elseif (typeof(b) == "number") then
			return new(a.x - b, a.y - b)
		else
			return new(a.x - b.x, a.y - b.y)
		end
	end,
	__mul = function(a, b)
		if (typeof(a) == "number") then
			return new(a * b.x, a * b.y)
		elseif (typeof(b) == "number") then
			return new(a.x * b, a.y * b)
		else
			return new(a.x * b.x, a.y * b.y)
		end
	end,
	__div = function(a, b)
		if (typeof(a) == "number") then
			return new(a / b.x, a / b.y)
		elseif (typeof(b) == "number") then
			return new(a.x / b, a.y / b)
		else
			return new(a.x / b.x, a.y / b.y)
		end
	end,
	__mod = function(a, b)
		if (typeof(a) == "number") then
			return new(a % b.x, a % b.y)
		elseif (typeof(b) == "number") then
			return new(a.x % b, a.y % b)
		else
			return new(a.x % b.x, a.y % b.y)
		end
	end,
	__pow = function(a, b)
		if (typeof(a) == "number") then
			return new(a ^ b.x, a ^ b.y)
		elseif (typeof(b) == "number") then
			return new(a.x ^ b, a.y ^ b)
		else
			return new(a.x ^ b.x, a.y ^ b.y)
		end
	end,
	__eq = function(a, b)
		if (typeof(a) == "number") then
			return (a == b.x) and (a == b.y)
		elseif (typeof(b) == "number") then
			return (a.x == b) and (a.y == b)
		else
			return (a.x == b.x) and (a.y == b.y)
		end
	end,
	__lt = function(a, b)
		if (typeof(a) == "number") then
			return (a < b.x) and (a < b.y)
		elseif (typeof(b) == "number") then
			return (a.x < b) and (a.y < b)
		else
			return (a.x < b.x) and (a.y < b.y)
		end
	end,
	__le = function(a, b)
		if (typeof(a) == "number") then
			return (a <= b.x) and (a <= b.y)
		elseif (typeof(b) == "number") then
			return (a.x <= b) and (a.y <= b)
		else
			return (a.x <= b.x) and (a.y <= b.y)
		end
	end,
	__tostring = function(a)
		return a.x .. " " .. a.y
	end,
	__unm = function(a)
		return new(-a.x, -a.y)
	end
}
mt.__index = mt
function new(x, y)
	return setmetatable(
		{
			__type = "vec2",
			x = x or 0,
			y = y or 0
		},
		mt
	)
end

return new

Your current code doesn’t need __newindex, so you could remove it (for now).

2 Likes