The function must have self parameter:
function test() return { foo = "bar", say = function(self) print(self.foo) end } end
Calls of the form t:say() actually always unfold in t.say(t) and if you want within the function say() have access to the fields of the table, you need to declare at least one parameter (with any name) in which you and this table will be transmitted. In Lua, the common name for such a parameter is self , but you can use any.
If the function has several parameters, the table will always be passed as a "zero" parameter, i.e. the call t:say(x, y) will be translated to t.say(t, x, y) and when declaring the function you always you should expect the self parameter in the first place:
say = function(self, x, y) print(self.foo) end
As for the syntax with a colon, this is so-called. syntactic sugar. In Lua, in fact, there are no classes, and they can only be emulated (to some extent) using tables. And so that this emulation looks similar to the use of classes, and such syntax was introduced.