Better Syntax?

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

Better Syntax?

Mike Jones
Hi,

I am working on a project where I have basically an event style
framework, and I would like some way to "chain together" functions. I
have had a quick look at OO libraries for Lua but they don't seem to
be suited to what I am trying to do. I can't seem to find an example
that fits my use case of wanting parrallel modules providing the same
functions.

Here is a short example of what I am doing at the moment:

-- core function, defined in common header
function dosomething(arg)
    print("Orginal dosomething()")
   return 0
end

-- module 1 included in script, adds extra code on to existing function
pre_module1_dosomething = dosomething
function dosomething(arg)
    print("Module 1 dosomething()")
    return pre_module1_dosomething(arg)
end

In my example the functions are in different files, but that's the
basic pattern I am currently using to chain extra code on to the end
of a function that has already been defined. When executed it should
output "Module 1 dosomething()" followed by "Original dosomething()",
ie both functions get executed. Is there a better way of achieving
this sort of functionality? the use of the "pre_module1_dosomething"
variable to hold the parent function seems very ugly to me.

- Mike Jones

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

云风 Cloud Wu


Mike Jones <[hidden email]>于2016年10月3日周一 下午6:44写道:
Hi,

I am working on a project where I have basically an event style
framework, and I would like some way to "chain together" functions. I
have had a quick look at OO libraries for Lua but they don't seem to
be suited to what I am trying to do. I can't seem to find an example
that fits my use case of wanting parrallel modules providing the same
functions.

Here is a short example of what I am doing at the moment:

-- core function, defined in common header
function dosomething(arg)
    print("Orginal dosomething()")
   return 0
end

-- module 1 included in script, adds extra code on to existing function
pre_module1_dosomething = dosomething
function dosomething(arg)
    print("Module 1 dosomething()")
    return pre_module1_dosomething(arg)
end

In my example the functions are in different files, but that's the
basic pattern I am currently using to chain extra code on to the end
of a function that has already been defined. When executed it should
output "Module 1 dosomething()" followed by "Original dosomething()",
ie both functions get executed. Is there a better way of achieving
this sort of functionality? the use of the "pre_module1_dosomething"
variable to hold the parent function seems very ugly to me.

- Mike Jones

How about this one ?

function dosomething(arg)
  print("Orginal dosomething()")
  return 0
end

local hook = {}

function hook.dosomething()
print("Module 1 dosomething()")
end

local function inject(original, hook)
for method, func in pairs(hook) do
local pre = original[method]
original[method] = function (...)
func(...)
return pre(...)
end
end
end

inject(_ENV, hook)

print(dosomething())

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Mike Jones
On 3 October 2016 at 13:04, 云风 Cloud Wu <[hidden email]> wrote:

>
>
> Mike Jones <[hidden email]>于2016年10月3日周一 下午6:44写道:
>>
>> Hi,
>>
>> I am working on a project where I have basically an event style
>> framework, and I would like some way to "chain together" functions. I
>> have had a quick look at OO libraries for Lua but they don't seem to
>> be suited to what I am trying to do. I can't seem to find an example
>> that fits my use case of wanting parrallel modules providing the same
>> functions.
>>
>> Here is a short example of what I am doing at the moment:
>>
>> -- core function, defined in common header
>> function dosomething(arg)
>>     print("Orginal dosomething()")
>>    return 0
>> end
>>
>> -- module 1 included in script, adds extra code on to existing function
>> pre_module1_dosomething = dosomething
>> function dosomething(arg)
>>     print("Module 1 dosomething()")
>>     return pre_module1_dosomething(arg)
>> end
>>
>> In my example the functions are in different files, but that's the
>> basic pattern I am currently using to chain extra code on to the end
>> of a function that has already been defined. When executed it should
>> output "Module 1 dosomething()" followed by "Original dosomething()",
>> ie both functions get executed. Is there a better way of achieving
>> this sort of functionality? the use of the "pre_module1_dosomething"
>> variable to hold the parent function seems very ugly to me.
>>
>> - Mike Jones
>
>
> How about this one ?
>
> function dosomething(arg)
>   print("Orginal dosomething()")
>   return 0
> end
>
> local hook = {}
>
> function hook.dosomething()
> print("Module 1 dosomething()")
> end
>
> local function inject(original, hook)
> for method, func in pairs(hook) do
> local pre = original[method]
> original[method] = function (...)
> func(...)
> return pre(...)
> end
> end
> end
>
> inject(_ENV, hook)
>
> print(dosomething())

Perhaps I should explain how my files are laid out in case there's a better way.

header.lua - this file is always included/executed/whatever first, it
is actually embedded in to the executable at compile time... so this
would contain the original dosomething() function
module1.lua - this would contain some replacement dosomething()
module2.lua - this might contain another dosomething()
application.lua - this is the main lua application, and would start
with something along these lines:
require("module1")
require("module2")
...

my application would "run" application.lua (which should just set up
the environment), it would then call dosomething() from C code

using your example I think I would put this in header.lua:
function dosomething(arg)
  print("Orginal dosomething()")
  return 0
end

function inject(original, hook)
  for method, func in pairs(hook) do
  local pre = original[method]
    original[method] = function (...)
      func(...)
      return pre(...)
    end
  end
end

then in each module file I could do something like:

module1={}
function module1.dosomething()
    print("Module dosomething()")
end
inject(_ENV, module1)

It's a bit tidier than what I am doing at the moment, and it also
opens up the possibility of doing something else with variables using
the same syntax by changing the inject function.

I'll wait until tomorrow before refactoring my code just in case
someone else has a better suggestion, but this should do the trick.

Thanks,
Mike

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Martin
In reply to this post by Mike Jones
On 16-10-03 03:43 AM, Mike Jones wrote:

> Hi,
>
> I am working on a project where I have basically an event style
> framework, and I would like some way to "chain together" functions.
>
> Here is a short example of what I am doing at the moment:
>
> -- core function, defined in common header
> function dosomething(arg)
>     print("Orginal dosomething()")
>    return 0
> end
>
> -- module 1 included in script, adds extra code on to existing function
> pre_module1_dosomething = dosomething
> function dosomething(arg)
>     print("Module 1 dosomething()")
>     return pre_module1_dosomething(arg)
> end
>
> In my example the functions are in different files, but that's the
> basic pattern I am currently using to chain extra code on to the end
> of a function that has already been defined. When executed it should
> output "Module 1 dosomething()" followed by "Original dosomething()",
> ie both functions get executed. Is there a better way of achieving
> this sort of functionality? the use of the "pre_module1_dosomething"
> variable to hold the parent function seems very ugly to me.

Hi,

I don't see problems in original approach. I'd just change
"pre_module1_dosomething = dosomething" to
"local pre_module1_dosomething = dosomething" to avoid spoiling
_G table.

The other way may be introducing global functions like

"_G.register_callback(method_name, func)"

  which stores handler function of given method and

"_G.handle_event(method_name, ...)"

  which sequentially calls all handlers for given method
  and returns result of handler which was registered first
  (or first not nil result - anything you choose)

so in in header file code should be like

register_callback(
  'dosomething',
  function(arg)
    print('Orginal dosomething()')
    return 0
  end
)

in module 1:

register_callback(
  'dosomething',
  function(arg)
    print('Module 1 dosomething()')
  end
)

And in users code:

handle_event('dosomething', ...)

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Soni "They/Them" L.
In reply to this post by Mike Jones


On 03/10/16 07:43 AM, Mike Jones wrote:

> Hi,
>
> I am working on a project where I have basically an event style
> framework, and I would like some way to "chain together" functions. I
> have had a quick look at OO libraries for Lua but they don't seem to
> be suited to what I am trying to do. I can't seem to find an example
> that fits my use case of wanting parrallel modules providing the same
> functions.
>
> Here is a short example of what I am doing at the moment:
>
> -- core function, defined in common header
> function dosomething(arg)
>      print("Orginal dosomething()")
>     return 0
> end
>
> -- module 1 included in script, adds extra code on to existing function
> pre_module1_dosomething = dosomething
> function dosomething(arg)
>      print("Module 1 dosomething()")
>      return pre_module1_dosomething(arg)
> end
dosomething = (function(dosomething) return function(arg)
   print "module 1 dosomething()"
   return dosomething(arg)
end end)(dosomething)

A syntax sugar for the above would be nice, yes. If we had lambdas using
"->" syntax, then:

dosomething = (dosomething -> function(arg)
   print "module 1 dosomething()"
   return dosomething(arg)
end)(dosomething)

>
> In my example the functions are in different files, but that's the
> basic pattern I am currently using to chain extra code on to the end
> of a function that has already been defined. When executed it should
> output "Module 1 dosomething()" followed by "Original dosomething()",
> ie both functions get executed. Is there a better way of achieving
> this sort of functionality? the use of the "pre_module1_dosomething"
> variable to hold the parent function seems very ugly to me.
>
> - Mike Jones
>

--
Disclaimer: these emails may be made public at any given time, with or without reason. If you don't agree with this, DO NOT REPLY.


Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Sean Conner
In reply to this post by Mike Jones
It was thus said that the Great Mike Jones once stated:
> Hi,
>
> I am working on a project where I have basically an event style
> framework, and I would like some way to "chain together" functions.

  To me, "event style framework" reads as "callbacks and promises" whereby
you pass around fragments of code to run as each event comes in.  For this
style, I use Lua coroutines to avoid the callbacks and to present code in a
straightforward way.  For example:

        function echo(socket)
          socket:write(socket:read("*a"))
          return echo(socket)
        end

My socket framework is event driven (backed by epoll()/poll()/select()
depending upon the Unix variant) but each connection is driven by a Lua
coroutine.  The socket:read() function will "block" until input is received,
then it resumes the coroutine.  the socket:write() will write as much out
until it would block, then (if it would and there's still output), it will
"block" until output can be written, with further output buffered.

  It makes writing networking code much nicer and it's easier to follow the
logic as it's imperative (and yes, I do use this at work where I'm
processing SIP messages).

  Now, on to your question.

> I
> have had a quick look at OO libraries for Lua but they don't seem to
> be suited to what I am trying to do. I can't seem to find an example
> that fits my use case of wanting parrallel modules providing the same
> functions.
>
> Here is a short example of what I am doing at the moment:
>
> -- core function, defined in common header
> function dosomething(arg)
>     print("Orginal dosomething()")
>    return 0
> end
>
> -- module 1 included in script, adds extra code on to existing function
> pre_module1_dosomething = dosomething
> function dosomething(arg)
>     print("Module 1 dosomething()")
>     return pre_module1_dosomething(arg)
> end

  I dug through my archives and I found the following bit of code (cleaned
up---I was starting to learn Lua):

        function fappend(f1,f2)
          return function(...)
            return f2(f1(...))
          end
        end

> In my example the functions are in different files, but that's the
> basic pattern I am currently using to chain extra code on to the end
> of a function that has already been defined. When executed it should
> output "Module 1 dosomething()" followed by "Original dosomething()",
> ie both functions get executed. Is there a better way of achieving
> this sort of functionality? the use of the "pre_module1_dosomething"
> variable to hold the parent function seems very ugly to me.

  You could get fancy (or scary, depending upon your viewpoint):

        _function_mt =
        {
          __concat = function(f1,f2)
            return function(...)
              return f2(f1(...))
            end
          end
        }

        debug.setmetatable(debug.getmetatable(print),_function_mt)

        dosomething = module1.dosomething .. module2.dosomething

        print = function(...)
          log('debug',"calling print")
          return ...
        end
        .. print ..
        function(...)
          log('debug',"done calling print")
          return ...
        end

  -spc (Betcha didn't know you could concat functions?)

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

steve donovan
On Tue, Oct 4, 2016 at 12:04 AM, Sean Conner <[hidden email]> wrote:
>   -spc (Betcha didn't know you could concat functions?)

I remember being very excited when I discovered that I could concat
functions. But alas it applies to ALL functions. I've put in the
'probably a bad idea' box, since every new global override has a
chance of messing up loaded libraries and making errors more obscure.

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Ką Mykolas
In reply to this post by Sean Conner
Blargh... This kind of wording sounds... kind a misleading. Just some sugar for expressing a function COMPOSITION as a concatenation.
 
> variable to hold the parent function seems very ugly to me.

  You could get fancy (or scary, depending upon your viewpoint):

        _function_mt =
        {
          __concat = function(f1,f2)
            return function(...)
              return f2(f1(...))
            end
          end
        }

        debug.setmetatable(debug.getmetatable(print),_function_mt)

        dosomething = module1.dosomething .. module2.dosomething

        print = function(...)
          log('debug',"calling print")
          return ...
        end
        .. print ..
        function(...)
          log('debug',"done calling print")
          return ...
        end

  -spc (Betcha didn't know you could concat functions?)


Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Miroslav Janíček
In reply to this post by Sean Conner

> On 4 Oct 2016, at 0:04, Sean Conner <[hidden email]> wrote:
>
> It was thus said that the Great Mike Jones once stated:
>> Hi,
>>
>> I am working on a project where I have basically an event style
>> framework, and I would like some way to "chain together" functions.
>
>  I dug through my archives and I found the following bit of code (cleaned
> up---I was starting to learn Lua):
>
> function fappend(f1,f2)
>  return function(...)
>    return f2(f1(...))
>  end
> end

It might be a good idea to handle cases in which f1 or f2 are undefined:

      function fappend(f1, f2)
        if f1 ~= nil and f2 ~= nil then return function(...) return f2(f1(...)) end
        elseif f1 ~= nil then return f1
        else return f2
        end
      end

(And maybe also calling the function “compose” would be more indicative of its intent.)

  M.


Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Sean Conner
It was thus said that the Great Miroslav Janíček once stated:

>
> > On 4 Oct 2016, at 0:04, Sean Conner <[hidden email]> wrote:
> >
> > It was thus said that the Great Mike Jones once stated:
> >> Hi,
> >>
> >> I am working on a project where I have basically an event style
> >> framework, and I would like some way to "chain together" functions.
> >
> >  I dug through my archives and I found the following bit of code (cleaned
> > up---I was starting to learn Lua):
> >
> > function fappend(f1,f2)
> >  return function(...)
> >    return f2(f1(...))
> >  end
> > end
>
> It might be a good idea to handle cases in which f1 or f2 are undefined:

  I disagree [1].  It makes *no* sense to call the function with any of the
parameters nil (it comes across as pointless) and at worse, it could hide
bugs in the code elsewhere (why is f1 nil?  How could that happen?). [2]

>       function fappend(f1, f2)
>         if f1 ~= nil and f2 ~= nil then return function(...) return f2(f1(...)) end
>         elseif f1 ~= nil then return f1
>         else return f2
>         end
>       end
>
> (And maybe also calling the function “compose” would be more indicative of its intent.)

  That I have no problem with.

  -spc

[1] And I feel I might be in the minority here.

[2] I'm firm in the "crash fast and crash loud" crowd here.

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Miroslav Janíček

> On 4 Oct 2016, at 17:44, Sean Conner <[hidden email]> wrote:
>
> It was thus said that the Great Miroslav Janíček once stated:
>>
>>> On 4 Oct 2016, at 0:04, Sean Conner <[hidden email]> wrote:
>>>
>>> It was thus said that the Great Mike Jones once stated:
>>>> Hi,
>>>>
>>>> I am working on a project where I have basically an event style
>>>> framework, and I would like some way to "chain together" functions.
>>>
>>> I dug through my archives and I found the following bit of code (cleaned
>>> up---I was starting to learn Lua):
>>>
>>> function fappend(f1,f2)
>>>  return function(...)
>>>    return f2(f1(...))
>>>  end
>>> end
>>
>> It might be a good idea to handle cases in which f1 or f2 are undefined:
>
>  I disagree [1].  It makes *no* sense to call the function with any of the
> parameters nil (it comes across as pointless) and at worse, it could hide
> bugs in the code elsewhere (why is f1 nil?  How could that happen?). [2]

You’re right in that for a general function composition function, this wouldn’t be correct (nil is not the identity function).

However, in this use-case, it might be practical. Suppose we have two modules, module1 and module2.

In module1:

    local function foo(...)
      -- etc
    end

    dosomething = fappend(dosomething, foo)

In module2:

    local function bar(...)
      -- etc
    end

    dosomething = fappend(dosomething, bar)

Calling dosomething() with both modules loaded would call both foo and bar (in the order in which they were loaded); if we only load one of them, things will still work.

I think something like this is what the OP wanted — but it’s quite likely that I misunderstood the problem!

>>      function fappend(f1, f2)
>>        if f1 ~= nil and f2 ~= nil then return function(...) return f2(f1(...)) end
>>        elseif f1 ~= nil then return f1
>>        else return f2
>>        end
>>      end
>>
>> (And maybe also calling the function “compose” would be more indicative of its intent.)
>
>  That I have no problem with.

So in the end, the nil-checking version should not be called “compose”, just the original, proper, composition function (the one you posted). Which is why I kept the name “fappend” in the snippets above.

  M.



Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Mike Jones
In reply to this post by Mike Jones
Thanks everyone for your suggestions, I think the fappend() approach
was what I was originally looking for but the inject method got me
thinking. After a little research I came up with this:

function table.clone(org)
    local new = {}
    for name, value in pairs(org) do
        new[name] = value
    end
    return new
end

function prepend_functions(target, source)
    for name, sourcefunc in pairs(source) do
        if type(source[name]) == "function" and type(target[name]) ==
"function" and source[name] ~= target[name] then
            local previousfunc = target[name]
            target[name] = function (...)
                local ret = sourcefunc(...)
                if ret ~= 0 then return ret end
                return previousfunc(...)
            end
        end
    end
end

include = require
function require(modulename)
    local before_ENV = table.clone(_ENV)
    include(modulename)
    prepend_functions(_ENV, before_ENV)
end

This lets me just call require("module1") and it will automagically
chain together any functions that get redefined.

Anyone see any flaws with this approach/with the code?

Thanks,
Mike

(I'm only starting to learn lua, but I am getting the impression that
"ugly hack" and "cool feature" are the same thing in lua? lol)

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Ericson Carlos

On Wed, Oct 5, 2016 at 3:14 AM, Mike Jones <[hidden email]> wrote:
​...

include = require
function require(modulename)
    local before_ENV = table.clone(_ENV)
    include(modulename)
    prepend_functions(_ENV, before_ENV)
end

This lets me just call require("module1") and it will automagically
chain together any functions that get redefined.

Anyone see any flaws with this approach/with the code?

Thanks,
Mike

(I'm only starting to learn lua, but I am getting the impression that
"ugly hack" and "cool feature" are the same thing in lua? lol)


​I think it's a cool feature in Lua that the namespace (scope?) is just another table. So taking advantage of this to roll your own module system is par.

I'd say leave 'require' as is and use 'include' for the new module function. Or maybe even name it something longer and more descriptive e.g. 'include_prepend', as you don't have to type it often.​

Reply | Threaded
Open this post in threaded view
|

Re: Better Syntax?

Hisham
In reply to this post by Mike Jones
On 4 October 2016 at 16:14, Mike Jones <[hidden email]> wrote:
> (I'm only starting to learn lua, but I am getting the impression that
> "ugly hack" and "cool feature" are the same thing in lua? lol)

It's an understandable impression to have, but there are good criteria
where to draw the line. If you're concerned with modularity (mixing
your code with other people's by loading libraries) and long-term
maintainability (no surprises for a future maintainer, who may be your
future self), some things here stand out as "red flags", namely the
_ENV tricks, and writing to standard API globals and tables.

The recommended style is for modules to return a table containing
their functions, instead of creating globals. This way, your chaining
function would receive two module tables (the original module and the
new one), and return the table of combined functions.

-- original.lua
local original = {}
function original.doSomething()
   print("original!")
end
return original

-- mod1.lua
local mod1 = {}
function mod1.doSomething()
   print("mod1!")
end
return mod1

-- main.lua
local prepend = require("prepend")
local original = require("original")
local mod1 = require("mod1")
local chained = prepend.chain(original, mod1)
-- you could of course chain more with
-- chained = prepend.chain(chained, mod2)
-- and so on
chained.doSomething()

-- prepend.lua
local prepend = {}
function prepend.chain(original, additions)
   local out = {}
   for name, func in pairs(original) do
      out[name] = func
   end
   -- This assumes that all functions in additions
   -- exist in original.
   for name, func in pairs(additions) do
      out[name] = function (...)
         -- replicating your chaining protocol here
         -- note it allows only one return value
         local ret = func(...)
         -- how about true as a stop-mark instead?
         if ret ~= 0 then return ret end
         return original[name](...)
      end
   end
   return out
end
return prepend

-- Hope this helps!

-- Hisham