I never really got a hang of using the Lua ecosystem to do practical programming.

A big hinderence often is the “no batteries” included nature of Lua, which by design has a very small core.

Lua has LuaRocks - The Lua package manager, which has plenty of packages to many practical things. Here I’m trying to sit down and “map the territody” using familiar set of tasks that I often do with Python

I installed luarocks using nix, which is my current driver – nix-env -i luarocks. I could have easily used brew install luarocks. The luarocks.org site has a search box to find packages that are registered with lua, but packages can be installed from elsewhere also.

I started with a simple markdown package:

➜  ~ luarocks install --local markdown 
Installing https://luarocks.org/markdown-0.33-1.src.rock

markdown 0.33-1 depends on lua >= 5.1, < 5.4 (5.1-1 provided by VM)
No existing manifest. Attempting to rebuild...
markdown 0.33-1 is now installed in /Users/pgowda/.luarocks (license: MIT/X11)

Note: if you don’t specify --local flag, luarocks will try to install it in “system” location – something I’d gladly avoid.

I then tried to use it from Lua REPL (I already had installed Lua):

$ lua 
Lua 5.4.3  Copyright (C) 1994-2021 Lua.org, PUC-Rio
> local md = require("markdown")
stdin:1: module 'markdown' not found:
        no field package.preload['markdown']
        no file './share/lua/5.4/markdown.lua'
        no file './markdown.lua'
        no file './markdown/init.lua'
        no file './lib/lua/5.4/markdown.so'
        no file './markdown.so'
        no file './lib/lua/5.4/loadall.so'
stack traceback:
        [C]: in function 'require'
        stdin:1: in main chunk
        [C]: in ?
> 

The issue above is that unlike Python (site-packages location, which is nice, but is its own can of worms), lua doesn’t know about luarocks location.

You can inform the REPL to look for files there by adding this line:

$ lua                                     
Lua 5.4.3  Copyright (C) 1994-2021 Lua.org, PUC-Rio
> package.path = package.path .. ';/Users/pgowda/.luarocks/share/lua/5.1/?.lua'
> local markdown = require('markdown')
> markdown("Hello, *world*")

<p>Hello, <em>world</em></p>

So, now we have the package available for programming.

Interestingly, when I imported the markdown package like this, I ran into issues:

local md = require 'markdown'
> md("Hello, *world*")
stdin:1: global 'md' is not callable (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: in ?

So, this means the local name of the package should match the original name of the package. I’m sure, I’ve tripped up against this couple of times and not realize what was happening.

The markdown.lua module1 exports the markdown function as a global:

-- For compatibility, set markdown function as a global
_G.markdown = markdown

Rest of the module (see 1 for source), is a good study in how to structure a module as a standalone script. This behaviour is very similar to how Python treats a module as a standalone script via the user of __main__.


  1. ~/.luarocks/share/lua/5.1/markdown.lua which you can see here online