How can I chain function call in lua?

I am trying to make a function to chain functions call. However it is not working. It should be able to print out the value after doing the sum without any issues.

Here is my first attempt:

local _ = {}
function _.add(x, y) return x + y end
function _.lt(x, y) return x < y end

function _.chain(value)
    local mt = {
        __newindex = function(t, k, v)
            t._value = _[k](t._value, v)
        end
    }
    local chain = {
        _value = value,
        value = function(self)
            return self._value
        end
    }
    chain = setmetatable(chain, mt)
    return chain
end

local value = chain(2).add(5)
print(value)

This solution is not working because it should be able to localize the function through the __newindex metatable . Instead of localizing the function, it’s throwing me a error saying the following message:

lua: main.lua:21: attempt to call a nil value (method 'add')
stack traceback:
    main.lua:21: in main chunk
    [C]: in ?

My second attempt at solving this issue was by using no metamethod what’s so ever:

local _ = {}
function _.add(x, y) return x + y end
function _.lt(x, y) return x < y end

function _.chain(value)
    local t = {
        _value = value;
        value = function (self)
            return self._value
        end
    }
    -- merge methods in t
    for k, v in pairs(_) do
        if type(v) == "function" then
            t[k] = function (self, ...)
                local result = v(self._value, ...)
                if result ~= self._value then
                    self._value = result
                end
                return self
            end
        end
    end
    
    return t
end
local sum = _.chain(2):add(5):value()
print(sum)

This attempt doesn’t work because it’s printing out nil instead of 7.

This is really simple! All you have to do is return the table in the function like so:

local module = {}

function module:test()
    print("Start of chain")
    return module
end

function module:chainedMethod()
    print("End of chain")
    return module
end

return module

Then you can just use it like so:

local module = require(path_to_module)
module:test():chainedMethod()

Your second method works just find for me (it printed out seven).


I believe the reason it errors for your first method is because of the use of __newIndex instead of __index. __newIndex only fires when setting and unknown key in the metatable, and you’re not setting a key, you’re indexing one:

local value = chain(2).add(5)
--       indexing "add" ^

The problem with what you’re trying to do is __index doesn’t provide passed arguments. This means that you’d have to edit the functions add and lt directly and use : so self is passed:

local _ = {}
function _:add(x) return _.chain(self:value() + x) end
function _:lt(x) return _.chain(self:value() + x) end

function _.chain(value)
    local mt = {
        __index = _
    }
    local chain = {
        _value = value,
        value = function(self)
            return self._value
        end
    }
    chain = setmetatable(chain, mt)
    return chain
end

local value = _.chain(2):add(5):value()
print(value) -- prints "7"

This is probably not what you wanted but either you attach a __call metamethod to the method names themselves this is as far as you can get (I can’t think of anything else).

1 Like