Lua - Sharing States between Module



Sharing states between Module is very easy as Lua by default caches a module when it is loaded for the first time. If we are returning a table from a module, we can use to store common state and modify the same accordingly across the modules. We can create a sharable table as well instead of creating a shared module. In this chapter, we're discussing the approaches to share data across the modules.

Example - Sharable Module Approach

This is much easier and idiomatic approch to manage states by returning a table as module. As a module is loaded only once, it will be cached and the table will be used commonly across the modules to share data.

Let's create a file shared.lua in current directory to act as application's shared data table.

shared.lua

-- common table
local state = {
  counter = 0,
  message = "Hello from shared state!"
}

function state.increment()
  state.counter = state.counter + 1
end

-- return the table as module
return state

Create modules which are to use the shared module.

module1.lua

local shared = require("shared")
print("Module 1 - Current counter:", shared.counter)
shared.increment()
print("Module 1 - Counter after increment:", shared.counter)
print("Module 1 - Message:", shared.message)

module2.lua

local shared = require("shared")
print("Module 2 - Current counter:", shared.counter)
shared.message = "State updated by Module 2!"
print("Module 2 - Updated message:", shared.message)

Let's now import both above modules in main code and see the impact of share module state.

main.lua

-- load both modules
require("module1")
require("module2")

-- load shared module
local shared = require("shared")

-- print the final values
print("Main - Final counter:", shared.counter)
print("Main - Final message:", shared.message)

Output

When we run the above program, we will get the following output−

Module 1 - Current counter:     0
Module 1 - Counter after increment:     1
Module 1 - Message:     Hello from shared state!
Module 2 - Current counter:     1
Module 2 - Updated message:     State updated by Module 2!
Main - Final counter:   1
Main - Final message:   State updated by Module 2!

As evident from the output, the shared modules is loaded in three modules module1, module2 and main but each module receives the same shared table. Changes done in one module are reflecting in other modules. This makes shared modules truely a sharable module.

Example - Sharable Table Approach

We can create a global shared table as well.

Let's create a file shared.lua in current directory to act as application's shared data table.

shared.lua

-- common table
_SHARED = _SHARED or { counter = 0, message ="Hello from shared state!" }

function incrementCounter()
  _SHARED.counter = _SHARED.counter + 1
end

Create modules which are to use the shared module.

module1.lua

require("shared")
print("Module 1 - Current global counter:", _SHARED.counter)
incrementCounter()
print("Module 1 - Counter after increment:", _SHARED.counter)
print("Module 1 - Message:", _SHARED.message)

module2.lua

require("shared")
print("Module 2 - Current global counter:", _SHARED.counter)
_SHARED.message = "State updated by Module 2!"
print("Module 2 - Updated message:", _SHARED.message)

Let's now import both above modules in main code and see the impact of share module state.

main.lua

-- load both modules and the shared one
require("module1")
require("module2")
require("shared")

-- print the final values
print("Main - Final global counter:", _SHARED.counter)
print("Main - Final global message:", _SHARED.message)

Output

When we run the above program, we will get the following output−

Module 1 - Current global counter:      0
Module 1 - Counter after increment:     1
Module 1 - Message:     Hello from shared state!
Module 2 - Current global counter:      1
Module 2 - Updated message:     State updated by Module 2!
Main - Final global counter:    1
Main - Final global message:    State updated by Module 2!

As evident from the output, the shared modules is loaded in three modules module1, module2 and main and are using a globally shared table. This approach is lesser preferred as it depends upon global variable _SHARED.

Preference of returning table instead of global table

  • Namespace Management − As each module provides its own namespace, naming conflicts are rare.

  • Dependency Management − When a shared module is explicitly loaded using require, it is easily trackable. In case of global, it is difficult to maintain.

  • Testing − Modules with external dependency are easier to test as compared to using global one.

Advertisements