suppose we have something like this:
local value = 1
local function get()
return value
end
local function double()
value = value * 2
end
return {
get = get,
double = double,
}
this is a pretty simple example of a "thing with methods and some state", and could be used like this:
local doubler = require "doubler"
doubler.double()
print(doubler.get()) --> 2
doubler.double()
doubler.double()
print(doubler.get()) --> 8
but what if we wanted multiple instances of this doubler thing?
a common approach is to make it a class:
local methods = {}
local function new()
return setmetatable({
value = 1,
}, {
__index = methods,
})
end
function methods.get(self)
return self.value
end
function methods.double(self)
self.value = self.value * 2
end
return {
new = new,
}
which could then be used like so:
local doubler = require "doubler"
local d1 = doubler.new()
local d2 = doubler.new()
d1:double()
d1:double()
print(d1:get()) --> 4
print(d2:get()) --> 1
that required a quite a lot of changes to the original module.
for instance, every field access now requires a preceeding self.
.
but today it occurred to me something like this would also work:
-- unchanged from the original
local value = 1
local function get()
return value
end
local function double()
value = value * 2
end
return {
get = get,
double = double,
}
local doubler = loadfile "doubler.lua"
local d1 = doubler()
local d2 = doubler()
-- then can be used like the class case
-- though with '.' instead of ':'
d1.double()
d1.double()
print(d1.get()) --> 4
print(d2.get()) --> 1
simply by changing require
to loadfile
, we get basically the same behaviour as the class-based approach, but with no refactoring necessary.
this works because load
and loadfile
, unlike require
, don't evaluate the loaded module immediately: they instead return a function that evaluates the module when run.
in our case, every time doubler()
is called, a new local value
is created, and two closures are returned with that new local bound as an upvalue.
sometimes people say things like "closures are just a poor man's objects", or "objects are just a poor man's closures".
i don't know what poor men have to do with anything, but this demonstration shows that the two do have an overlap of usecases.
but is it good?
pros:
- no changes to our original module were required
- no
self.
qualification clutter needed - rather simple
cons:
- it's kind of confusing if you don't know this behaviour of
loadfile
- it forces you to have one class per file... like java
- doing something that is semantically a method call but which uses
.
instead of :
looks like a mistake (1) - it's an uncommon and unidiomatic way to do this in lua: just looking at
doubler.lua
you might not realize there are going to be multiple instances of this value
local in existence
overall i'm not sure i'd ever use this in a real project, even though it is kind of cool.
also, considering how simple it is, i'm surprised i've never seen it before.