require(AssetId) should return Module as it's second return value

I’m making a script that handles requiring modules from the client using AssetIds (Filtered properly, don’t worry!) and I can’t return the Module object, or parent the Module where I want. While I could probably do this with InsertService, since require() already inserts, why not just let us use the object from the insert?

Not looking for hacky solutions, I know enough already, just suggesting a convenience addition to require()

This would be a security risk. Private modules required via require(AssetId) would now have their instances exposed when they didn’t before

And why exactly can’t you return the module object?

Script:

print(require(123456))
> MainModule

ModuleScript:

return script
3 Likes

I thought @Seranok explained that private modules already had their instances exposed, that’s why we have the security recommendations for making “private” modules the way they are now.

The returning of the module is an interesting idea, which makes a ton of sense. Will work much better than the insert service.

Private ModuleScripts are currently not entirely secure – that is correct. Because of that, is it justifiable to make them even less secure? Those who can crack into private ModuleScripts in their current state are far and few between. By making it that simple to grab the module’s instance, now there are a plethora of people who can do that, and regardless of what kind of new type of private module ROBLOX might introduce, there exist old private ModuleScripts that aren’t using that and all of a sudden have their contents exposed.

If returning the module’s instance from within the module solves the issue for you, I don’t see why it would be a good idea to implement the suggested feature.

Can’t return the Module object, along with it’s useful functions. “14:52:32.957 - Module code did not return exactly one value” This limits me to one return value, making that module useless, as I will only be able to return the Module, and not the Table/Function as well, giving no exposed access. Nice though, but non-functional.

And perhaps just return the parent argument for free to take modules? I don’t see that being an issue at all.

You’ve got to think outside the box.

--ModuleScript
return {Instance=script, Data=returnValue}

It’d be weird to have special behavior like that unless there’s absolutely no other alternative (which doesn’t appear to be the case). Tbh if modules needed a change it would be allowing require to return multiple values.

2 Likes

My script is intentionally made to function like a normal module, and it’s return value is a function, not a table. Putting a function inside of a table will create clutter, and as it’s a public intended module, it would reduce it’s readability and usability by a huge amount. While I could get it to work, it would require a large overhaul of the way the data structure is organized to make it feel fluid at all after something like that.

And I agree, allowing modules to return multiple values make a ton more sense than conditional behavior, though I would be fine with either.

If you need a large overhaul, you’re doing it wrong. Make a generic module that handles the change for you.

--ModuleDataHandler
local module = {}

function module:UnpackData(returnedTable)
    return unpack(returnedTable)
end

function module:CondenseData(...)
    return {...}
end

return module

When you want to return something from a module:

--Random module script
local a,b,c,d,e,f
return ModuleDataHandler:CondenseData(a,b,c,d,e,f)

When you want to use a returned value from a module:

--Random script
local a,b,c,d,e,f = ModuleDataHandler:UnpackData(require(randomModuleScript))

This requires no significant amount of work. To be honest, you don’t even need the ModuleDataHandler and could instead use unpack(require(module)) and return {a,b,c,d,e,f} depending on what you’re doing. Having a module to handle that for you will allow you to do special stuff if need be though.

The coding style you proposed goes against a lot of the fundamental rules of my style. While it would work, it doesn’t match how I code, and it would be insanely out of place. When I said it would reduce readability and usability without a redesign, I meant what I said, and I’m not doing it wrong. Code shouldn’t be forced to work, and just coded for whatever is most practical. There is a time to code for readability instead. Why can’t you just take my word for it that I know how my code is organized, and understand that I don’t want to pack it into a table? You don’t need to push your style on other people, as we all have our own styles and sometimes it’s incompatible.

Exactly what I was trying to avoid here. The previous posts of yours were suggesting solutions and workaround that were not hacks. This is a hack.

Why should ROBLOX need to sacrifice its security for your bad practices?

5 Likes

Because this has literally nothing to do with how your code is organized.

Okay, I’m bailing out. It’s become obvious your goal here isn’t to solve your code’s problem.

if you don’t want to use a table, you can use a function

-- module
return function() return data, script end

-- script
local data,script=require(module)()

force exposing the instance of all existing modules is not ok.

3 Likes

Modules are already insecure.

As I said earlier, being able to return tuples would be a good solution for me as well, and would not reduce security.

Should have been obvious from the get-go. I already mentioned InsertService, which is a perfect solution to the problem, and yet there is some need in your to go against the purpose of the thread.

I can think of about 4 different ways to code this, all of which are not hacks, and all of which perfectly mesh with my style. I was not looking for solutions, but I was suggesting a convenience change to the API.

For the reason mentioned above with returning a table, this would disrupt my scripting style, and the way that the module is meant to be used. Solutions like this are not what I’m looking for, I’m suggesting a new feature for Roblox

On topic:
I would like to amend the purpose of this thread to instead request Modules to be able to return multiple arguments, as that would be way more in line with my coding style, and it would make much more sense from a usability perspective as well.

While I do not agree with this api change, I don’t think “private modules” should be taken into consideration. IIRC, they were not intended in the first place and only happened through accident.

The idea of a private module itself is a security risk. You should never let people run arbitrary code within your game.

1 Like

Yet here we are in a world of closed-source programs/libraries that we all use on a day-to-day basis. Isn’t it ironic to suggest that when ROBLOX itself uses closed-source libraries? This is definitely getting to be a discussion that’s way out of the scope of this thread, but using a closed-source module is the user’s choice and responsibility – it’s a tradeoff between security and having functionality they wouldn’t otherwise be able to have.

If someone doesn’t want to risk security, then they won’t use a private module – good for them. If they find the functionality provided worth the potential risk, then security is a non-issue. The majority of the time I see private modules is in cases where the place owner can’t program to begin with.

2 Likes

Module scripts by default return a table. Ignoring coding styles, why is a table not sufficient as a return object?

ie: When would you ever need to use a tuple over a table for any purpose other than styling?

There’s a pretty straightforward solution to this:

local module = {}

module.GetChildren = function()
  return script:GetChildren()
end

module.SomeOtherFunc = function()
-- todo implement
end

return module
2 Likes

This would make things less clear from a usability perspective, it adds another difference between RBX.Lua and Lua, and introduces additional complexity for essentially no reason.

You can just do this if you want to return multiple values.

-- In Module.lua
local value = nil
-- Todo: implement this
return {value, script}

-- In OtherScript.lua
local value, script = unpack(require(Module.lua))
1 Like

Because Coding styles.

And the solutions above are appreciable, but I’m making a generic module loader, and so I’d have to make every module I’m using need to have these changes made to them, when these modules are already being used in other projects as they are, and are also supposed to be used just as they are. It seems weird to change them for only one project’s benefits, especially when it’s against my coding style to needlessly package something into a table, just to be unpacked again.

What’s the application of that? To have a module in a module? I guess this would work, but just using InsertService like I said above is still easier for me.

Valid point. I wasn’t thinking of this.

Why does Lua have multiple return values at all, when tables exist?

Really though. Roblox’s require function is already unrecognizably different from it’s vanilla counterpart. Requiring a module is no more confusing than calling a function. The return value of a module can already be any single value with no problems. Hell, writing and requiring a module is basically like writing and calling a function. Until you’re hit by a “Module code did not return exactly one value” error. Why are we resisting multi-value returns for modules?