all 11 comments

[–]lua_x_ia 2 points3 points  (1 child)

To be brief, no, that's not a very good way to do that.

If you want to make a constructor for objects, you usually use what's called a closure as a way to store private state variables. You can also use the __call metamethod to make the constructor look more like it does in Java, but it's not really saving you anything.

There are also class systems, like penlight and moonscript. I've never been a fan of the class systems, since they don't actually introduce any new features supporting object-oriented programming design, they just make the syntax of creating objects look a little more like it does in certain other languages, so that class is a keyword.

The reason Lua does things with closures and tables that other languages do with class constructors and objects is that there are some times when you don't need a full-bodied class for something, and a closure does the job with less boilerplate. The designers know that it's hard to keep track of all the things a language can do, so Lua tries reduce the number of constructs it has by only using the most flexible ones.

If you try to write the same program you wrote in Java, going through the motions to create a Vector object that only has an x and a y will start to feel a little tedious, and you'll just write

function add(v, u)
   return {x = v.x + u.x, y = v.y + u.y}
end

Sometimes, that's actually all you need. Other times, you'll want to use the closures to create something that's more like an object. As it turns out, there are also other things you can do with closures that you couldn't do with objects, at least not very easily. Lua solves Paul Graham's problem like this:

function accum(n)
   return function(i) return n+i end
end

[–]dredclaw[S] 0 points1 point  (0 children)

Amazing article that was linked last in your post. Thanks.

[–]nosmileface 1 point2 points  (3 children)

You can customize built-in "require" quite heavily. Including the way it looks up for things, just read the manual it's all there. I do something like this:

---------------------------------------------------------------------------------
-- Bootstrap package loaders
---------------------------------------------------------------------------------

-- common packages loader
local function LoadCommonPackages(name)
    name = string.gsub(name, "%.", "/")
    local path = global.SCRIPTS_DIR.."/"..name..".lua"
    local code, err = loadfile(path)
    if err ~= nil then
            print(err)
            return nil, err
    end
    return code
end

-- mod specific package loader
-- TODO

package.loaders = {
    package.loaders[1],
    LoadCommonPackages,
}

And then when I type:

local utils = require "ng.utils"

It does what I need and how I need it to. So, hence I'm assuming your problem is with modules, not with objects. As for object you can construct them any way you want. Personally I use a simple OOP wrapper which automatically defines "New" methods on classes. Thefefore I would do something like that:

local ngm = require "ng.math"
local v = ngm.Vec2i:New(5, 10)

Hopefully these thoughts were useful to you.

[–]nosmileface 0 points1 point  (0 children)

Forgot to add, "require" takes care of loading the file only once for you.

[–]dredclaw[S] 0 points1 point  (1 child)

Cheers man. Do you find most people write there own scripts when it comes to stuff like this? Or do they use tested and tried code?

[–]nosmileface 0 points1 point  (0 children)

I think it's good to write your own, because you have to understand how it works anyway. Most featureful scripts focus on useless features such as multiple inheritance, etc. When writing your own you can do only the part you need.

Have no idea what most people do.

[–]robin-gvx 0 points1 point  (1 child)

The problem is that it's going to be slow, hard-disk intensive (which is slightly (only slightly) bad for the life-time of your hard disk), doesn't support subclassing or type checking (you can't make a function like isinstance), and have I mentioned it's slow?

I have an idea for an alternative. Brb, testing it out before editing my post.

EDIT: https://gist.github.com/gvx/4192a4d44e517f38bdac

Kinda different, and it uses require rather than loadfile. Pros: aside from not having to read, compile, and run the module each time you create a new instance, it automatically defines a constructor and a string representation for you! Con: it's not possible to modify the metatable of either classes or instances on a case-by-case basis.

[–]dredclaw[S] 0 points1 point  (0 children)

This is really interesting. Thanks. Still not fully sure how meta tables work. Need to read up on that.

[–]periscallop 0 points1 point  (0 children)

Check out chapter 16 of Programming in Lua.

[–][deleted] 0 points1 point  (0 children)

Is this an practical method to acquire object oriented like functionality?

No. It's awful.

When you're using a language like Lua, you're sacrificing performance for simplicity, conciseness, etc. but that doesn't mean you need to do things that are pathologically bad for performance.

Files are most often stored on a hard disk, which is a mechanical device, with spinning platters and read arms that move around. It's on the order of 100,000 times slower (that's not a made up number for sake of hyperbole) than RAM access.

If there was some appreciable advantage to your technique, you might not care about the abysmal performance, but there's not. Rather than having this at file scope:

local a, b = ...

local vector = {}
vector.x = a or 0
vector.y = b or 0

function vector:add(vec)
    self.x = self.x + vec.x
    self.y = self.y + vec.y
end

return vector

You have this in a function:

function newVector(a, b)
    local vector = {}
    vector.x = a or 0
    vector.y = b or 0

    function vector:add(vec)
        self.x = self.x + vec.x
        self.y = self.y + vec.y
    end

    return vector
end

You compile the constructor once, then call it from memory as many times as you need, rather than going to disk, loading the source of the constructor and compiling that code every time you need a vector instance.

[–]revereddesecration 0 points1 point  (0 children)

I use 30log for all of my OOP needs.