Lua - Immutable Lists



In Lua, in order to make a list immutable, we can use metamethods and metatables. In this chapter, we'll demonstrate how to make a list immutable.

We'll be using __newindex method, which blocks the entry of new elements to the list.

readOnly function

function readOnly (t)
   local proxy = {}
   local mt = {       -- create metatable
      __index = t,
      __newindex = function (t,k,v)
         error("attempt to update a read-only table", 2)
      end
   }
   setmetatable(proxy, mt)
   return proxy
end

readOnly function when applied on any Lua object, makes it immutable. In our scenario, now we can initialize the list in constructor function then pushing an element will be throwing an error as shown in the example below:

Complete Example - immutable Linked List

In below example, we're constructing a list and then making it immutable using readOnly function and then while pushing an element, we're getting error.

main.lua

-- List Implementation
list = {}
list.__index = list

setmetatable(list, { __call = function(_, ...)
   local t = setmetatable({ length = 0 }, list)
      for _, v in ipairs{...} 
         do t:push(v) 
      end
      return t
end })

-- push an element to the end of the list
function list:push(t)
   -- move till last node    
   if self.last then
      self.last._next = t
      t._prev = self.last
      self.last = t
   else
      -- set the node as first node
      self.first = t
      self.last = t
   end
   -- increment the length of the list
   self.length = self.length + 1
end

-- iterate through the list
local function iterate(self, current)
   if not current then
      current = self.first
   elseif current then
      current = current._next
   end
  
   return current
end

function list:iterator()
   return iterate, self, nil
end

 function readOnly (t)
      local proxy = {}
      local mt = {       -- create metatable
        __index = t,
        __newindex = function (t,k,v)
          error("attempt to update a read-only table", 2)
        end
      }
      setmetatable(proxy, mt)
      return proxy
end

-- create a new list with values
local l = readOnly(list({1}, {2}, {3}, {4}, {5}))

print("Original List")
-- iterate throgh entries
for v in l:iterator() do 
   print(v[1]) 
end

-- try to modify the list
l:push({{7}})

Output

When we run the above code, we will get the following output−

Original List
1
2
3
4
5
lua: main.lua:19: attempt to update a read-only table
stack traceback:
	[C]: in function 'error'
	main.lua:49: in metamethod 'newindex'
	main.lua:19: in method 'push'
	main.lua:66: in main chunk
	[C]: in ?
Advertisements