What is a Maid object?

I don’t know where I found it or what my sudden urge to learn about it is, but here I am.

There seems to be a Roblox-specific pseudo-object that developers are creating called Maid (in RoStrap, it’s Janitor). I think I may have gained a general gist from reading the code and obscure posts (there are no threads that directly discuss what Maid is), but I’m still not fully certain of what I’ve read.

If anyone’s familiar with Maid/Janitor, I would like some confirmation, correction of misconceptions or some knowledge on these objects. Apparently they’re very useful and I’d like to solidify my knowledge before I step further.

I could just be overthinking it altogether or not thinking properly. The answer could be right on this thread and I skimmed over it.


RoStrap’s Janitor

What I have right now:

  • An object intended to do clean up for any object.

What I don’t understand:

  • “This implementation covers all use cases, as it doesn’t force you to rely on naive typechecking to guess how an instance should be cleaned up. Instead, the developer may specify any behavior for any object.”

    • From reading the code, it seems that you can pass a string specifying the method to remove a Janitor indice by, which defaults to Destroy. That string is used as an indice to the object for a method call (e.g. Instance["Destroy"](Instance)).
  • What’s it’s purpose?

Nevermore’s Maid

What I have right now:

  • An object intended to do clean up for any object

What I don’t understand:

  • GiveTask and setting an indice in the Maid seem to do the same thing? What’s the difference?

    • From a bit of reading, it seems that GiveTask just adds a certain value you pass to a table. Is it named this way for conventional reasons and to fit in with the class name of “Maid”?

DevForum

What I got from reading posts:

  • Acreol:A temporary fix until Roblox actually patches it could be to store all of the connections to Player signals in a table and disconnect them all when the player leaves the game (basically a maid that cleans on player removing)”

  • DataBrain: “I would create a helper table, or maybe even a simple Maid class where you can collect connections, instances, etc. that need to be removed at a later time.”

  • Quenty: " Using a maid object to connect to events and then cleanup state is really nice, something that would generally require another object if you didn’t have this abstraction."

    • (What is an abstraction?)

Cheers for any replies. :tada:

20 Likes

From my understanding, janitors and maids are used for mass routine cleanups of objects.


RoStrap

Instead of automatically assigning cleanup methods through typechecking,

if typeof(Object) == "Instance" then
	MethodName = "Destroy"
elseif typeof(Object) == "RBXScriptConnection" then
	MethodName = "Disconnect"
end

RoStrap let’s you directly state which method to call on an object when going through the cleanup procedure.

Janitor:Add(Object, "Destroy") --Calls the "Destroy" method on an object when cleaning up
Janitor:Add(Object, "Destroy", "Part") --Same as the above but sets an index that can be removed later without having to go through the full cleanup process
Janitor:Add(Object, true) --Calls the object itself when cleaning up

Notice how you’d only need to use one line instead of three lines.

This may be useful when your game is using custom class objects, in which case, you’d be able to manually handle it’s cleanup behavior without type checking.

Let’s say you have a round based game that restarts the game as soon as the round ends. When the round ends, you’d also want to get rid of everything that wasn’t originally in the map when the round started (players, bullets holes, ammo boxes, health kits, etc.).

Instead of looping through all the tables in self.ExternalObjects to call the destroy method, you can just add the objects to the janitor as soon as they’re created.

All objects will be cleaned up with a single call this way.

local RoundController = {}
RoundController.__index = RoundController

function RoundController.new()
	local self = setmetatable({}, RoundController)
	
	self.TimeLeft = 2677 --colbert2677
	self.Map = "DevForum" 
	
	
	self.Garbage = Janitor.new()
	
	self.ExternalObjects = {
		PlayerModels = {},
		BulletHoles = {},
		AmmoBoxes = {},
		HealthKits = {}
	}
	
	return self
end

function RoundController:AddExternalObject(Type, Object)
	table.insert(self.ExternalObjects[Type], Object)
	self.Garbage:Add(Object, "Destroy")
end

function RoundController:ResetRound()
	self.TimeLeft = 3000
	
	self.Garbage:Cleanup()
	self.Garbage = Janitor.new()
end

return RoundController

Nevermore

:GiveTask and setting an indice is the same thing, except you’d only have to set the indice using one line. Maid:GiveTask(Object)

I think it’s just named that way because maids are usually tasked with performing cleanup duties for a household.

12 Likes

Maid, as originally conceived, was a away to manage event connections in a concise manner.

The usual pattern for dealing with event connections was to assign the connection to a variable, then later disconnect it when needed.

local mouseDown = Mouse.Button1Down:Connect(...)
local mouseUp = Mouse.Button1Up:Connect(...)
local mouseMove = Mouse.Move:Connect(...)
local mouseIdle = Mouse.Idle:Connect(...)

...

mouseDown:Disconnect()
mouseUp:Disconnect()
mouseMove:Disconnect()
mouseIdle:Disconnect()

For a large number of connections, this could be difficult to keep track of. Maid was created to manage connections all at once while also retaining familiarity with the previous pattern:

Maid.mouseDown = Mouse.Button1Down:Connect(...)
Maid.mouseUp = Mouse.Button1Up:Connect(...)
Maid.mouseMove = Mouse.Move:Connect(...)
Maid.mouseIdle = Mouse.Idle:Connect(...)

...

-- Disconnect a particular connection (neat!)
Maid.mouseIdle = nil

-- Disconnect everything.
Maid:DoCleaning()

The purpose of GiveTask was to assign connections without needing a name. It’s basically shorthand for an incremented integer as the key:

Maid[#Maid.Tasks+1] = Mouse.Button1Down:Connect(...)

Later the concept was expanded to manage functions, instances, and other managers, as seen in modern implementations.

37 Likes

Good question, lots of good info I never knew I needed.

An abstraction is the concept and attempt of decoupling function from state, utility from environment, what it does from how it’s used, all in pursuit of making programming simpler to understand for our feeble minds. This is found literally everywhere in the field of computer science.

Some examples:

  • Functions and Loops decouple execution of code from where it’s located. The alternative is copy-pasting code.

  • Variables decouple the data itself from where it’s located. Tables do this too, to a higher extent. The alternative is probably referencing its raw memory address.

  • Objects combine the benefits of both functions and tables to create a singular entity. Tables and objects are interchangeable terms in Lua due to first-class function paradigm.

  • Coroutines decouple execution of code from when it’s executed. The alternative is awkwardly and confusingly layering code into a single thread.

  • Metatables decouple operations from the functions they represent. The alternative is defining methods in an object-like manner, which isn’t too bad.

  • Scope decouples variables from their security and clean-up. The alternative is manual garbage collection, x = nil.

  • getfenv decouples variables from where they are located, similarly to variables.

  • Programming Languages decouple logic from the machine code behind it.

Maids are also an abstraction, since they decouple garbage collection from how and when this is done.

I will note that in most cases, abstractions couple functions to new states rather than removing the state entirely. You can never truly decouple a function from all states, only recoupling it to a more manageable one. It’s like choosing a material to write on: Although some are easier to use than others, you can’t write on nothing. Therefore, abstractions are not useful in all cases, like metatables and classes, and their attention to detail can cause more recoupling than decoupling if used carelessly.

And yes, I only replied to answer that one question.

11 Likes

Im confused why do we need to have instances like parts as a maid

no idea xD. Just makes it look cooler