How to make your modules wayyyyy better!

Hello, I have been relearning luau to come back to roblox scripting, I am currently messing with modules, I have learnt some stuff about how the script editor prediction works, how to make your modules easier to mess around with, better to work with and better optimized. Using meta"tables", using “self” which I wish I can explain in a better way, and way more.

So let’s go with some of the stuff.


Using “if false then” and how it’s useful.

First of all, I wanna talk about the script editor predictor for functions in modules. One thing that you should learn is that, the script editor caching for modules IGNORES all If statements, meaning that you can’t give specific functions when you’re in a Local script or normal script. Basically any code inside your module that has a if statement will just run. Even if it’s a “if false then”.

So you can have specific returns for the script editor, using “if false then”. Example:

function module:TestingScriptEditor()
    if false then
        return {
            specificScriptEditorFunc = function() end;
        }
    end
end)

This would return a prediction for the script editor with the function “specificScriptEditorFunc”.

What can you do with this?

Well, if you want to make a public module, this will help you and your module users learn your module easily. A lot of times when messing with metatables, you might not get predictions of the functions you’re gonna use. Like if you’re putting the functions inside the module and just redirecting the functions that were called from another table, which would optimize your module and help not cause memory leaking. I will explain how to do that later on this post.

I like to do this on my module, not only for people that would use my modules, but also for me personally. It makes your scripting way faster and easier. If you have a big and known module and you don’t have this implemented I would highly recommend you to do so.

If you want some examples of how this works, you can check my module, which contains this method, on the GetDataStore function.

Anyways, I think I said enough about prediction. Let’s go to using self and metamethods (metatables).


How self is useful and metamethods/metatables

If you don’t know metatables, you can tie events to a table basically. So for example when you reference something which doesn’t exist on a table you can tie an event to it, and do something about it. The most known use of that, is having one table INSIDE your module which contains your functions and redirecting your nil indexes to those functions.

I also do that on the module I linked above.

One thing that you might not realise is what self can be used to.

Self is basically the table from where the function was called from. This might sound useless, but when you tie this to the method above, what you can do is have info that you need on that table that you’re returning to the script, like for example in my module, I keep data about the DataStoreName on it, and I get the datastore on the functions using self and checking the info about the DataStoreName.

Example:

local moduleMethods = {
    getWhatever = function(self, ...)
        return self["infoName"]
    end
}

function module:Get(infoNameGiven)
    if false then
        return {
             getWhatever = function(self, ...) end)
        }
    end
    local metatable = {
         __metatable = true; --lock metatable
         __index = function(Table, index)
             return moduleMethods[index]
         end;
    }
    local toReturn = {
        infoName = infoNameGiven;
     }
     setmetatable(toReturn, metatable)
     return toReturn
end)

Just a reminder that when calling the function from another script you will need to use “:”, if you don’t it will not work, also you need to set self as the first paremeter on that function.

Another metamethod which is really cool is __call, it fires whenever someone calls a table as a function, aka using () on the end. You can get the parameters, just like a function, this is an example of how you would use it.

local metatable = {
    __call = function(Table, ...)
        -- code to do something / return something
    end;
}

This metamethod is used for example when you’re using DataStore2 on which you can do DataStore2(“dataName”, player) to get a DataStore. Sadly this metamethod is bad when using predictions. The script editor just like anything will ignore stuff from metatables. Causing you to not be able to get any predictions from that. So I would highly recommend you to have also another function which does the same, so that you can get auto-completing on these functions.


Separating your module into various pieces

You might have already faced an situation where your module got extremely long and annoying to mess around with, a lot of modules out there will have these be all separated into various parts that are all required by the mainModule. Like a Settings “module” which contains settings modifiable by the developer using your module. That is helpful for people that have to constantly update your module, for the most part they would be able to just replace the settings module with their already existing one and just keep the new version of the MainModule.

But this is not JUST for stuff that the scripter using your module will have to mess around with, for example, you just have your methods, just like I showed before in a sub-module. Which keeps your main code way cleaner and easier to read, and just better over-all when you wanna update something. And stuff like that is not that hard to mess around with.


Using require(assetId)

I personally don’t like requiring using the assetId. But if you wanna add compatibility, you will have to understand that requiring it will cause the script to yield until it gets your module, which can cause problems for example, on stuff like PlayerAdded, which might not fire because the module took a while to load and now one player is already in the game, and you didn’t detect him joining, so you would have to do a for loop on existing players, so that’s why I don’t like it. Now I could be wrong here, but the module might also not replicate to other scripts, instead it would load a new version. (I think).

But if you wanna add assetId requiring, make sure your primary module is called MainModule, else it won’t go through. Also people won’t be able to mess with the settings easily by requiring unless you allow settings to be modified from the module itself.

Requiring by assetId is only available for server.


I think that’s it? Anything new I will add here, if you have a tip which I might have not talked about I would love if you mentioned it down below. If I said anything wrong contact me! :P

Thanks for reading, have a nice day developing!

7 Likes

By the way, soon I will be adding some examples via images, I didn’t do it now because I’m on my phone and as I said on multiple posts I don’t have my own computer, I use my dad’s one and so I need to wait until night. You can expect some better examples using images in some hours.

I FORGOT SORRY

AND IT WAS NEVER DONE YAY

1 Like

Really enjoyed reading this, keep it up!

1 Like