Logical operations metamethods

By logical operations I mean “and”, “or”, and “not”

My use case is that for my custom Signal module I would like to be able to do things such as:

local s1 = Signal.New()
local s2 = Signal.New()

(s1 and s2):Connect(function()
-- fires after both s1 AND s2 are fired
end)

(s1 or s2):Connect(function()
-- fires after either s1 OR s2 is fired
end)

Alternatively I think the most correct way to do this would be:

local s1 = Signal.New()
local s2 = Signal.New()

(s1*s2):Bind(function()
-- fires after both s1 AND s2 are fired
end)

(s1+s2):Bind(function()
-- fires after either s1 OR s2 is fired
end)

But I think this seems less intuitive

A lot of this doesn’t really make sense to practically use. s1+s2 would invoke the __add metamethod and (s1 and s2) would just return s2 because of how ternary operators work. Unfortunately there’s not much you can do with variadic variables other than calling with them as the arguments and assigning them.

--Assignment to variables / returning multiple variables from a function
local part,position,normal,material = workspace:FindPartOnRayWithIgnoreList()

--Used as arguments in a function
function printEverything(...)
    local t = {...}
	for i = 1,#t do
		print(t[i])
	end
end
printEverything(1,2,3,4,5,6,"a","b","c")

If you want something to happen because of two conditions then you’re better off polling. Here’s an example of polling that could achieve the same result.

local var1 = false
local var2 = false


spawn(function()
    local start = tick()
	repeat
		wait()
	until var1 and var2
	var1 = false
	var2 = false
	
	--Your code
	print("I waited",tick()-start,"seconds for this to print!")
end)


--debug code given as an example
wait(10)
var1 = true
wait(1)
var2 = true

-->>> I waited 10.936549663544 seconds for this to print!

Please note that this is rudimentary code and the inaccuracy is due to the massive yield (wait()).

You can already achieve the (s1*s2) with not that much more code than what you already wrote.

local Signal1 = Signal.new()
local Signal2 = Signal.new()

local function functionToConnectTo()
	
end

Signal1:Connect(functionToConnectTo)
Signal2:Connect(functionToConnectTo)

Essentially you connect both of the events to the same function. This doesn’t take up twice the memory merely because you’re connecting both events to one variable which references one function.

2 Likes

Yes

I’m asking for a feature to overload this just how you can overload addition

I don’t really understand how this relates to what I’m requesting

Yea you’re right about :Bind (same as Roblox’s :Connect) but if instead it’s :Wait() then its a little more complicated


I think you misunderstand what I’m asking, I’m not asking for Roblox to implement this into their BindableEvent class, I will be implementing this with multiplication and addition into my own Signal class but I believe it would be cleaner if it were using the logical operations instead

Essentially you’re wanting to perform some sort of variadic equivalent but through that or gate. You don’t have to comment on it if it seems meaningless to you. :slight_smile:

I do know what you’re talking about. I don’t really see a use case for it though, as there are already ways to do this. If you can provide some use cases that don’t include syntax candy, I would definitely like to know!

I’m commenting on it because I want to understand

I still don’t understand how this is variadic because it’s only two parameters
Also it wouldn’t go through an or gate it would overload that and just call the function instead

Can you provide a use case of when metamethods aren’t syntax candy?

Just for reference, generally speaking these should be the opposite way around where * represents and and + represents or.

You can see this in the following example using binary.

AND (logical conjunction)
1 * 1 = 1
1 * 0 = 0
0 * 1 = 0
0 * 0 = 0

OR (logical disjunction)
1 + 1 = 1
1 + 0 = 1
0 + 1 = 1
0 + 0 = 0

Compare these with the results of an and and or operation on booleans.

2 Likes

Ya typo ;3

1 Like

The fact that and, or, and not work the way they do is a great feature. They’re predictable and have no side effects. I would really dislike it if the logical operators could be overloaded because that makes it harder to write some things.


For example, it’s common to have code like this

local value = condition and a or b

If and and or were unpredictable and could have side effects, we would instead have to write

local value
if condition then
    value = a
else
    value = b
end

This gets even worse if not can be overloaded. Then we can no longer write code like this:

if not condition then
    -- do things
end

Instead we have to write

if condition then
else
    -- do things
end

since not won’t necessarily be consistent anymore and we want to avoid side-effects.


Somewhat-similar arguments can be made for other operators, but the other operators are not used in such fundamental ways. The other operators, even before metamethods, had different results based on what type you used them on. The logical operators have the consistent results for every type (except bool) and are used in a type-agnostic way. Changing this would make writing new code harder (as shown above) and could break existing code.


If you want to get something similar to overloading and, or, and not, then just overload __mul, __add, and __unm which corresponds to a * b, a + b, and -a

4 Likes

But overloading is optional and you can consciously choose what to overload so this would not break old code and for new code you are making a conscious decision about it, furthermore if they are only overloaded when both parameters are tables with the same metatable (similar to __eq) then your ternary statements would still work

As for creating public modules you wouldn’t abuse this in a way that would make it confusing which I assume is the same principle that needs to be followed with any other metamethod - or part of lua

Signal.All(s1, s2):Connect(…)
Signal.One(s1, s2):Connect(…)

Or something along those lines would be much more clear to me (I wouldn’t need to enter your Signal.lua to find out what s1+s2 and s1*s2 mean) and accomplishes the same thing.

1 Like

Idk I think you would still need to know what it does (I would for clarification at the very least)

Even if you don’t, you still need to enter the module for getting the function name since intellisense doesn’t work on my modules because of their organization

So a 2 word comment on the operator wouldn’t add much extra and imo it looks much cleaner

Overloading is not possible. There is no metamethod to invoke; internally, or and and statements compile to their equivalent if x and if not x long representations.

1 Like

compile to their equivalent if x and if not x long representations.

It doesn’t use the gates?

Also is there anything I can read besides the lua source code to learn about this?

There aren’t “gates” really. A TEST or TESTSET instruction is used to compare the truthiness of the values, followed by a JMP to outside.
A x or y might look like…
GETGLOBAL
TEST
JMP
GETGLOBAL

Where it first loads the x global into the register and tests whether it evaluates to true or not. If it’s true, the TEST will do nothing, and the JMP will be hit to skip over the GETGLOBAL, otherwise the TEST skips over the JMP and then runs the GETGLOBAL, loading y into the register instead.

There used to he a pdf called No-Frills or something like that with a lot of information on the internals but the luaforge page for it went down some time ago.

1 Like

Is it this? http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf

Also if the metamethods were to be made, would it still be bad because it would slow down everything else since everytime a logical operation were made it would have to check if the metamethod is active?

Lua DOES have a table->flags optimization used for optimizing heavy usage of metamethods, so the speed difference if the metamethod wasn’t present wouldn’t be grave, but it’d still not work well because it’d mean that both if statements and the or/and keywords would be affected, since they both work the same way internally.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.