7

Is there a way to have

  • Two Lua modules (let's call them A and B)
  • Each module uses functions from the other, so they must require each other
  • A third module (let's call it C) can use A but not B e.g.

C.lua:

local A = require 'A'

-- ...

A.foo()
  • There may be another module D that requires B but not A and/or E requiring both A and B
  • Neither A nor B nor their members should be added to the global namespace.
  • Avoid using the module and setfenv functions (deprecated in Lua 5.2)

Related: Lua - how do I use one lib from another? (note: this solution does not handle circular dependencies.)

Community
  • 1
  • 1
finnw
  • 47,861
  • 24
  • 143
  • 221

3 Answers3

6

I found quite a simple way to do it:

A.lua:

local A = {}
local B

function A.foo()
    B = B or require 'B'
    return B.bar()
end

function A.baz()
    return 42
end

return A

B.lua:

local B = {}
local A

function B.bar()
    A = A or require 'A'
    return A.baz()
end

return B
finnw
  • 47,861
  • 24
  • 143
  • 221
  • Is there any harm in calling `require` from within a function like this? I've only ever seen it used at the top level. – finnw Nov 23 '11 at 20:49
  • 5
    There's no warning in the [reference manual](http://www.lua.org/manual/5.1/manual.html#pdf-require) about it, so I think it's safe. – The Student Dec 06 '11 at 11:19
  • 2
    You can use `require` anywhere in the code. For example, I often use this idiom to start the RemDebug at a given point in file: `if somecondition then require 'remdebug.engine'.start() end` – Michal Kottman Dec 06 '11 at 22:31
3

Another method, suggested by Owen Shepherd on the lua-l mailing list:

If we set package.loaded[current-module-name] at the top of each module, then any other module required later can refer to the current (possibly incomplete) module.

A.lua:

local A = {}
package.loaded[...] = A

local B = require 'B'

function A.foo()
    return B.bar()
end

function A.baz()
    return 42
end

return A

B.lua:

local B = {}
package.loaded[...] = B

local A = require 'A'

function B.bar()
    return A.baz()
end

return B

This will not work everywhere. For example if B's initialization depends on A.baz then it will fail if A is loaded first, because B will see an incomplete version of A in which baz is not yet defined.

finnw
  • 47,861
  • 24
  • 143
  • 221
  • 1
    if you don't want to call require the whole time (in each function as described by [finnw' other solution](http://stackoverflow.com/a/8248862/1162609)), it seems this is the only way left to go. Imho it's quite nice too since the `return modename` at the end of your module is written to `package.loaded[...]` anyway (using this structure of not polluting global namespace and returning tables from modules) – Gunther Struyf Apr 28 '16 at 15:18
3

A standard way to do this in any language is to introduce a mediator. Modules can then publish and subscribe to the mediator. http://en.wikipedia.org/wiki/Mediator_pattern

An example of this in my languages is mvccontrib bus, IEventAggregator, and MVVM Lite Messenger class. They all do the same thing.

Leblanc Meneses
  • 3,001
  • 1
  • 23
  • 26