Be lazy! Bind methods with this funny function!

We’ve all been there. We’ve written HttpService:GenerateGUID() one too many times and we’ve had enough. We want to assign it to a short hand variable like guid so we can do guid(). But NO! Lua says this is bad and slaps back with a Expected ':' not '.' calling member function GenerateGUID. You go to your development corner and cry, not willing to type the method again…

Get up, developer! It’s easy to make it shorter! You just have to bind it!

Now, I don’t particularly know if there’s already a tutorial pointing this out (I tried searching and couldn’t find one immediately so I’m writing one up here, though I’m sure I’m not the first so feel free to point one out in the replies though!) and I’m not even sure that this is what this concept is called, but I’m using JavaScript terminology here so here we go.

If you don’t know what JavaScript’s bind does, it creates a new function that sets the this keyword in the scope of that new function to whatever you pass to bind. Here’s an example:

const obj = {
    x: 123,
    getX() {
        return this.x;
    }
};

const unboundGetX = obj.getX;
const boundGetX = obj.getX.bind(obj);

console.log(unboundGetX()); // undefined
console.log(boundGetX()); // 123 

(example graciously stolen from MDN)

This new function that you’ve created can also accept the arguments that the function would normally accept.

Now, why am I explaining a JavaScript concept for a Roblox Lua tutorial? Well, we can apply this same concept here!

Let’s look back at our original example:

local guid = game:GetService("HttpService").GenerateGUID

print(guid()) -- error :(

We can write a bind for this pretty easily to get what we want:

function guid(...)
    return game:GetService("HttpService"):GenerateGUID(...)
end

print(guid()) -- {3A19CE6F-FCF5-4F61-9B8D-1392EA142B63}
print(guid(false)) -- 333A620D-8EBC-4796-8732-16B09BDDCF70

It works identically to how GenerateGUID works normally, but it’s now a smaller call. Great!

But… what if you want to apply this to many functions? Well, writing what we just wrote above over and over is very inconvenient. Let’s write a function that cleans up the boilerplate a bit:

function bind(method, self)
    return function(...)
        return method(self, ...)
    end
end

(this is the funny function)

This function lets you do the equivalent of above in a shorter way:

local h = game:GetService("HttpService")
local guid = bind(h.GenerateGUID, h)

print(guid()) -- {396F3800-D4A1-4FCB-A4BF-D6FB67BA2972}
print(guid(false)) -- 0E0DD5AA-1486-4C50-9F83-978096106AE0

And there you go. No more repeated calls to HttpService:GenerateGUID()!

You may be asking why this works. It’s good to be curious, so let me explain.

Lua’s : is syntactic sugar. When you do the above call to GenerateGUID, internally, it looks a little more like this:

local h = game:GetService("HttpService")

h.GenerateGUID(h) -- is the equivalent to h:GenerateGUID()

The first argument to a method like this is called self, which contains a reference to the actual module or class that houses the method. You can write these yourself in two ways:

local obj = {}

obj.x = 5

function obj:Test()
    return self.x
end

function obj.Test(self)
    return self.x
end

Both of these are equivalent. You can call both with the standard : sugar as well (e.g., obj:Test()obj.Test(obj)).

In the bind function, we’re taking advantage of the fact that you can pass whatever you want as self when you call the method with a . instead. When we do bind(h.GenerateGUID, h), we’re writing a function like this:

local guid = function(...)
    h.GenerateGUID(h, ...)
end

(by the way, ... means variadic arguments, so the function will accept any amount of arguments and pass them along to the original method!)

Which is pretty close to what we started with, except it’s a little less pretty. So, the reason why this way of doing things works is that we’re just calling the original method like a function (since you can’t pass methods as arguments to other functions) and passing the original module as its first argument, which means that self becomes whatever the original module is. In our case, self becomes HttpService, which is required for GenerateGUID to work.

8 Likes

This is my first time ever seeing (GenerateGUID) this so if you can give me some examples of what this would be great for that would be much appreciated.

You can just search it up

https://developer.roblox.com/en-us/api-reference/function/HttpService/GenerateGUID

it just generates a random string after taking some parameters

1 Like

It’s just the example I had on hand so it’s not the only thing this can be used for, but GenerateGUID generates a globally unique identifier. The function returns a string that is randomly generated that should be safe to use as an ID without checking for conflicts, since the chance of a conflict is astronomically low.

1 Like

Oh, ok thank you. I understand now.