プログラミングの魔物

エラー、バグ、仕様変更と戦うブログ

Luaのモジュールを穴が空くまで見つめてみる

Luaで組むならモジュールは避けて通れない。必須と言ってもいい。
今回はそんなモジュールについての解説・・・というか、自分が今理解している内容の確認のためにプログラムを組んでみる。


追記:
Lua5.2からはコンセプトが変わり、module関数とsetfenv関数は廃止されたらしい。
この記事を書いた時点ではLua5.1のコンパイラを使用していたので、改めて5.2のコンパイラに対応したモジュール定義を追記した。
ただし3行目の「_G[_NAME] = _M」についてはLua5.1に合わせただけなので、実際にはrequireから戻り値のテーブルを受け取るのが良いだろう。
※まだLua5.2コンパイラに触れたばかりなので、モジュール定義に冗長な部分があるかも知れない。

mod1.lua(Lua5.1)

module(..., package.seeall)

--モジュールの内容
function show()
	print(_NAME.."モジュールです")
end

mod1.lua(Lua5.2)

local _M = {}
local _NAME = ...
_G[_NAME] = _M
function _M:show()
	print(_NAME.."モジュールです")
end

return _M

main.lua

--モジュールの読み込み1
require "mod1"

--モジュールの読み込み2
mod2 = (function (...)
		local _NAME = ...
		local _M = {}
		_G[_NAME] = _M
		package.loaded[_NAME] = _M
		setmetatable(_M, {__index = _G})
		setfenv(1, _M)

		--モジュールの内容
		function show()
			print(_NAME.."モジュールです")
		end

		return _M
	end)("mod2")  --モジュール名を引数として無名関数を実行し、最後にテーブルを戻り値として返す

mod1:show()  --> mod1モジュールです
mod2:show()  --> mod2モジュールです

見ての通りmod2はただのテーブルであり、かつ、mod1と同じ機能を持ったモジュールである。
つまりLuaのモジュールとは「初回のロード時に一度だけ実行される関数の中で作られるテーブル」のことである。

実際のモジュールとは違いmod2ではパッケージ名を表す_PACKAGEの作成など省略している部分もある。
グローバル変数に関してはmodule関数の場合"."で区切られた名称ごとに要素を確保しているので「_G[modname] = _M」より少し複雑である。
また、mod2は代入しなくてもグローバル変数に登録されるが、この例では代入の形にしないとステートメントが成立しないのでエラーになる。

mod2はpakage.loadedに登録しているので、次からはrequire "mod2"でも取得できる。