lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Apr 15, 2013, at 6:18 AM, Matthew Wild wrote:

> People can and will [monkey-patch] anyway. It often breaks things - for example
> reloading plugins must clean up after themselves, 

Reloading *modules* is broken in a world where everybody has squirreled away 

  local ipairs=ipairs
  local module1=require("module1")
  local xpairs=module1.xpairs

  function give_me_referential_integrity_or_give_me_death()
    assert(module1.xpairs == xpairs)
  end  

The canonical way to be reload-friendly is for the module1 code to get any existing module1 table and mutate it. But that doesn't help with the "local xpairs" problem.

This is fixable outside core. Instead of the above pattern, write:

  local ipairs, module1, xpairs
 
  on_any_module_reload(function()
    ipairs=_ENV.ipairs;
    module1=_ENV.require("module1");
    xpairs=module1.xpairs
  end)

and trigger it off a monkey-patched require...but that's so ugly we're going to do one of the following:

  • Something really gross with debug.*
  • Run all of our code through m4.
  • Pull a java and say, "Source code isn't for humans, it's a serialization form for IDEs."

Having just spent a good day or so writing/debugging gcc __asm__ blocks inside #defines I do have some sympathy for Gosling's point about the C preprocessor throwing our source code into an unstructured pile-of-ASCII jail. There's really no way of reasoning locally about any bit of C source, which is the non-marketing reason Java doesn't have #define.

Anyway, I think there's some kind of interaction with initialization hoisting too, since although we don't really want #define, we don't really want variables either; we want "equate".

  static ipairs;
  import module1; -- sugar for static module1 = require("module1")
  static module1.xpairs;

...which create new bindings as "local ipairs,module1,xpairs" would. The reason I use "static" is I want something like this to work:

  for l in io:lines() do
    static validSsn = regexp.pattern("&?@#some horrible thing")
    if validSsn(l) then print(l) end
  end

where the semantics are

  local _$local1 = regexp.pattern("&?@#some horrible thing")
  for l in io:lines() do
    local validSsn = _$local1;  [...]
     
An alternative might be to cache per call-point, but in the case of little languages, you really want the regexp compiler to run at chunk load time so it can point out your syntax errors.

Why am I bringing this up in the context of reloading? Because it seems like the static initializers are the stuff you definitely want to rerun on reload. 

  local _local$1, _local$2, [...]

  local _$local1
  local function _$local1_init() 
    _$local1 = regexp.pattern("&?@#some horrible thing") end
  end
  _$local1_init()
  for l in io:lines() do ... end
  [...]

and have some mechanism to call all the _$local_init functions.

Jay