Log In  


This function creates simple extensible 'types' with a few additional (potentially) handy properties. If you find it useful, help yourself to it. Credit appreciated but not required.

  -- 52 tokens
  function new_type(names)
     local names = split(names, ' ')
     local meta = {}
     return setmetatable(
	meta,
	{
	   __call=function(self, ...)
	      local obj = {...}
	      for i=1,#names do
		 obj[names[i]] = obj[i]
	      end
	      return setmetatable(obj, {__index = meta})
	   end
	}
     )
  end

You use it like this:

  -- a 2d point object
  point = new_type('x y') -- argument names separated by spaces

  p = point(1, 2)
  print(p.x) -- prints 1
  print(p.y) -- prints 2

As for the handy properties, the first is that the object's data is stored both by key and as an array. Which means you can access the properties using the dot notation, numerical indexes, or even unpacking the object, all of which can come in handy in different situations.

  p = point(1, 2)

  x, y = unpack(p) -- extract all the data in order

  -- all of these print 1
  print(p.x)
  print(p[1])
  print(x)

  -- and all of these print 2
  print(p.y)
  print(p[2])
  print(y)

The downside of this is that each object is storing the data twice and, therefore, requires twice as much space. So if you have a lot of active objects in your program at once you'll run out of memory about twice as fast. But you can always modify new_type() to only use one or the other of these methods instead of both if you prefer.

The second handy property is that functions created with new_type() aren't actually functions, they're objects which you can add methods to. Those methods are then available to every object of that 'type'. For instance:

  point = new_type('x y')
  point.add = function(a, b)
     return point(a.x + b.x, a.y + b.y)
  end

  p = point(1, 2)
  q = point(2, 3)
  r = p:add(q)

  print(r.x) -- prints 3
  print(r.y) -- prints 5

It even works if the methods are defined after you've already created the objects:

  point.dot_product = function(a, b)
     return a.x * b.x + a.y * b.y
  end

  print(p:dot_product(r)) -- prints 13
3


1

Here's another version of the same thing. This one uses more tokens (99) but avoids duplicating the data in each object and therefore also avoids the duplicate data getting out of sync with itself:

-- 99 tokens
function new_type(names)
   local names = split(names, ' ')
   local meta = {}
   local key_map = {}
   for i=1,#names do
      key_map[names[i]] = i
   end
   return setmetatable(
      meta,
      {
         __call=function(self, ...)
            local obj = {...}
            return setmetatable(
               obj,
               {
                  __index=function(self, i)
                     if meta[i] then
                        return meta[i]
                     else
                        return self[key_map[i]]
                     end
                  end,
                  __newindex=function(self, key, value)
                     if type(key) == 'string' then
                        rawset(self, key_map[key], value)
                     else
                        rawset(self, key, value)
                     end
                  end
               }
            )
         end
      }
   )
end

So now you can change the object via the named property or the numerical index and you're actually just changing the same piece of data in either case:

point = new_type('x y')
p = point(1, 2)
print(p.x)  -- prints 1
print(p[1]) -- prints 1

p.x = 7

print(p.x)  -- prints 7
print(p[1]) -- prints 7

p[1] = 4

print(p.x)  -- prints 4
print(p[1]) -- prints 4


[Please log in to post a comment]