RBXScriptSignal:connectargs()

This would be a method to all RBXScriptSignal objects (for those of you unaware, any event is an RBXScriptSignal). It would return an RBXScriptConnection as usual but the difference would be that it would be fired with additional arguments supplied by the developer. In essence:

Connection = Part.Touched:connectargs( function(defaultArgs, suppliedArgs) local otherPart = unpack(defaultArgs) local two, four = unpack(suppliedArgs) end, 2, 4 )
The idea is that the programmer can supply the event with extra arguments to pass to the callback function when it fires. This decreases code repetition when you need to connect similar objects’ events, but you want slightly different behavior depending on other factors.

3 Likes

why have a default and supplied args? just pass one Tuple, and have it override based like normal

ex, make the source of the method like this:

function ConnectArgs(Self,Function,...)
	return Self:connect(function() Function(...) end)
end

and use case like:

function DoStuff(A,B,C)
	--X
end

local Connection = Part.Touched:ConnectArgs(DoStuff,A,B,C)

I think its a wonderful idea, but roblox isn’t nessecarily change happy with lua, so I’m uncertain on if it will be done

2 Likes

[quote] why have a default and supplied args? just pass one Tuple, and have it override based like normal

ex, make the source of the method like this:

function ConnectArgs(Self,Function,...)
	return Self:connect(function() Function(...) end)
end

and use case like:

function DoStuff(A,B,C)
	--X
end

local Connection = Part.Touched:ConnectArgs(DoStuff,A,B,C)

I think its a wonderful idea, but roblox isn’t nessecarily change happy with lua, so I’m uncertain on if it will be done [/quote]

Because roblox might want to add more arguments to an event and then everyone’s scripts would break.

1 Like

only if your function accepts the extra arguments not documented without overriding, which I would see as bad practice anyway, since all args are added to the back, and not the front, it would be illogical to depend on a variable being nil at the end of the enumerated function arguments

What’s wrong with closures?

for i = 1, 3 do local j = i^i; game.Workspace.BasePlate.Touched:connect(function(other) print(i, j, other); end); end

Just works as expected once a touch event occurs:

3 27 Right Leg 2 4 Right Leg 1 1 Right Leg

If needed, just calling a “large” handler function in an one-line closure with all needed parameters would be very short and doesn’t need some “connectargs” method. Short enough I’d say.

There’s several problems, mostly stylistic, and issues with copy/pasteing the functions, and not being able to re-use them, but personally, I use anonymous functions in a regular connection to achieve what I want, although you do have that slight overhead in lua from anonymous functions, which has gotten me before, I was writing a super lean loop in lua, and it was the anonymous function call that was consuming 20% of my processing capability, and I had no real way around it other than to rewrite my code, which I did end up doing, and it came out three hundred lines longer, and was a mess to read. that being said, I made heckuva efficient code that day :slight_smile:

1 Like

Mega Bump, but I can’t make a new thread so this will have to do.

I constantly run into problems where I have to sacrifice speed and memory to create a new lambda just so I can pass some parameters through the callback.
This comes up again and again in OOP and other event-driven code. For example, since I can’t pass ‘self’ for the function I would have to create a lambda just to do a namecall:

function Class:OnHeartbeat(DeltaTime)
	...
end

function Class:Init()
	RunService.Heartbeat:Connect(function(DeltaTime)
		self:OnHeartbeat(DeltaTime)
	end)
end

Ugliness aside, what’s worse is that lambda won’t cache in memory, because it uses self as an upvalue, so it just stays in memory. If we could pass arguments through (Assuming they go before the callback args), both problems could easily be solved:

function Class:Init()
	RunService.Heartbeat:Connect(self.OnHeartbeat, self)
end

Another case can be seen with chained events, such as the ‘PlayerAdded/CharacterAdded’ idiom. In the OnPlayerAdded callback, we might want to pass both the Character model and the Player instance so we can use its interface, like waiting for its CharacterAppearance to load:

local function OnCharacterAdded(Player: Player, Character)
	if Player.CanLoadCharacterAppearance and not Player:HasAppearanceLoaded() then
		Player.CharacterAppearanceLoaded:Wait()
	end
	...
end

local function OnPlayerAdded(Player)
	if Player.Character then
		OnCharacterAdded(Player, Player.Character)
	end
	
	Player.CharacterAdded:Connect(function(Character)
		OnCharacterAdded(Player, Character)
	end)
end

Again, that lambda for CharacterAdded will not be cached, keeping it in memory for the entire life of each Player (In this code, I could and have used Players/GetPlayerFromCharacter, but this seems extraneous and obviously shows some design flaw). If we could pass Player as an arg directly, all the problems go away.


There are a multitude more situations where this implementation would help, but for the brevity of this post I’ll limit it to the two above. For the sake of better code, please engineers look into this. Thank you.

3 Likes

The only thing I want from events is the sender, and on changed events, the new and old values

Sender can probably be added with ConnectSender etc.

2 Likes

I keep coming up with this in code. I really like the idea of having connect and once take in a vararg and pushing those arguments as parameters for the callback.

Here’s a snippet that could benefit from this

function self.Intro:In()
	intro.parent = PlayerGui
	
	gradIn:Play()
	gradIn.Completed:Once(function()
		labelIn:Play()
	end)
end

Could be changed to

function self.Intro:In()
	intro.parent = PlayerGui
	
	gradIn:Play()
	gradIn.Completed:Once(labelIn.Play, labelIn)
end

The only downside I can think of is that the PlaybackState from .Completed is also passed as an argument, but that shouldn’t be a problem because it should ignore it and analysis can catch that.

1 Like