Fairly functional data structures

The beauty of Lua, or Javacript, or pick your favorite scripting environment, is the tremendous amount of flexibility you have in doing things.  And thus, there are tens of ways to create a ‘class’, or doing ‘object oriented’ programming in these environments.  I like to keep things simple and consistant though.

For data structure construction, there are a few things that I keep in mind, or rather some constraints and desires that I have:

Creating an instance of the structure should be as simple as a function call with the structure’s name.  Although I can implement a ‘struct.new()’ function, I want to construct my struct with ‘mystruct = struct()’ and that’s it.

I need to be able to set some defaults on the ‘class factory’ so they can be used at various other times.

I want to be able to easily add things like operators and the like where it makes sense.
With these specs, how might a tackle a simple “Point” class?


Point = {
  defX = 0;
  defY = 0;
}

-- set the '__call()' meta method for easy construction
setmetatable(Point, {__call = function(self, ...) return Point.new(...) end}

Point_mt = {
  __add = function(self, other)
    return Point(self.x + other.x, self.y + other.y);
  end,

  __mul = function(self, s)
    -- swap arguments around if necessary to take care of both
    -- point * scalar and scalar * point
    if type(self) == "number" then
      self, s = s, self;
    end

    return Point(self.x*s, self.y*s);
  end,
}

Point.new = function(x, y)
  local obj = {x or Point.defX, y or Point.defY}
  setmetatable(obj, Point_mt);
  return obj;
end

local p1 = Point();
local p2 = Point(3,4);
local p3 = p2 + p1;
local p4 = p3 * 3;

Ok, that’s nice and all. But what the heck? OK, there are just a couple of critical pieces to get straight with this stuff. It has to do with the assignment of metatables. Take that first one for instance where the only thing the metatable has is the ‘__call()’ function. What does that do?

Well, pick a table, any table, and try to ‘call’ it as if were a function:

tbl = {}
tbl(params);

You’ll get an error stating that the thing is a table, and not callable. Well, the “__call()” meta method takes care of this. If you set it on a table, the table is suddenly callable? Meaning, if you use that ‘()’ syntax, as if it were a function, then whatever you implement in the function behind it will get called.

This is fairly convenient, as we can easily just defer to the Point.new() function, and get a new instance of our intended structure.

So, that takes care of the first part of wanting to call the thing as if it were a function. How about doing something with that structure factory. By default, the x and y values are set to zero because the .new() function gets these values from the ‘Point’ table. What if I wanted to change the default for all subsequent calls?

Point.defX = 10;
Point.defY = 15;

p5 = Point();
print(p5.x, p5.y);
>10, 15

In this way I have essentially changed a field that is associated with the ‘class factory’, not with an instance. That’s a fairly useful thing sometimes as well, particularly when you need to be able to change something class wide, without having to change much code.

But still, what about that second ‘setmetatable’? Whereas the first one was attached to the class factory itself “Point”, the second one gets attached to an instance of the class “obj”. These are two completely different things.

Once you do this combination, you have satisfied my original requirements.

Advertisements


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s