AntonPetrochenko
AntonPetrochenko

Reputation: 13

Lua internals: how do string methods work as methods?

Lua has a piece of syntactic sugar that allows for OOP methods to be implemented: the operator : when used as foo:bar(biz) is equivalent to foo.bar(foo,biz). However, what I've noticed is that it is also actively used by the standard string library in this manner:

local a = "a normal string"
local b = a:reverse() -- ????????
print(b)
-- outputs: gnirts lamron a

The question is: how exactly is this achieved? Naturally, when you try to assume that it expands to a.reverse(a) it falls apart, as a is a string and cannot possibly contain the key reverse, moreover, the statement is actually equivalent to string.reverse(a). My hunch was that it actually expands to _G[type(a)].reverse(a), but then you remember that the same stuff happens in the io library on file descriptors...

Upvotes: 1

Views: 495

Answers (2)

koyaanisqatsi
koyaanisqatsi

Reputation: 2793

Every string in Lua (5.1 - 5.4) has a metatable with metamethod __index.
Thats the place where all string functions are present.
Here is a function that shows it...

-- help()
help=function(help)
dump(debug.getmetatable(help).__index)
end

Thats very simple, isnt it?

EDIT: Because i forget the dump function :-) ...

-- dump(table)
dump=function(...)
local args={...}
local test,dump=pcall(assert,args[1])
if test then
for key,value in pairs(dump) do
  io.write(string.format("%s=%s\n",key,value))
  io.flush()
end
  return true
else
  return test,dump
end
end

Theres a string called _VERSION - Lets have a look on it...

>help(_VERSION)
reverse = function: 0x565c4870
lower = function: 0x565c4ab0
sub = function: 0x565c7020
byte = function: 0x565c6d30
gsub = function: 0x565c7bb0
char = function: 0x565c4d80
format = function: 0x565c5060
unpack = function: 0x565c62d0
gmatch = function: 0x565c6ee0
dump = function: 0x565c5a90
upper = function: 0x565c47e0
find = function: 0x565c7ba0
len = function: 0x565c44d0
rep = function: 0x565c4900
pack = function: 0x565c66e0
packsize = function: 0x565c61c0
match = function: 0x565c7b90

Thats the reason why it works as methods with . and :

Upvotes: 1

aschepler
aschepler

Reputation: 72271

The colon syntax really is equivalent to the dot syntax plus self parameter. So the question is, how does a.reverse work?

The Lua 5.4 manual explains:

The string library provides all its functions inside the table string. It also sets a metatable for strings where the __index field points to the string table. Therefore, you can use the string functions in object-oriented style. For instance, string.byte(s,i) can be written as s:byte(i).

We can see these metatable details with the code:

local a = "a normal string"
strindex = getmetatable(a).__index
if type(strindex) == "table" then
  print "string has metatable entry __index:"
  for k,v in pairs(strindex) do
    local same = (v == string[k])
    print(k, type(v), "same as string."..k..":", same)
  end
end

local b = a.reverse(a) -- ????????
print(b)
-- outputs: gnirts lamron a

Try it online!: the output is

string has metatable entry __index:
rep function    same as string.rep: true
byte    function    same as string.byte:    true
sub function    same as string.sub: true
char    function    same as string.char:    true
gmatch  function    same as string.gmatch:  true
format  function    same as string.format:  true
reverse function    same as string.reverse: true
match   function    same as string.match:   true
dump    function    same as string.dump:    true
len function    same as string.len: true
packsize    function    same as string.packsize:    true
find    function    same as string.find:    true
unpack  function    same as string.unpack:  true
upper   function    same as string.upper:   true
pack    function    same as string.pack:    true
lower   function    same as string.lower:   true
gsub    function    same as string.gsub:    true
gnirts lamron a

Upvotes: 2

Related Questions