https://matt.blwt.io/post/lua-the-little-language-that-could/ Matt Blewitt About Now RSS Twitter Mastodon GitHub Lua: The Little Language That Could 2023-05-28 Lua is probably my favourite "little language" - a language designed to have low cognitive load, and be easy to learn and use. It's embedded in a lot of software, such as Redis, NGINX via OpenResty and Wireshark. It's also used as a scripting language in games such as World of Warcraft and Roblox via Luau. This post is a brief love letter to the language, with some examples of why I like it so much. Simplicity Lua is not a complicated language, with relatively few features and not a lot of syntax to learn. There are eight types: * nil * boolean * number * string * userdata (used to represent C data structures, or blocks of raw memory) * function * thread (used for coroutines) * table (an associative array, and the sole data structure in Lua) There is no need to worry about float, int, usize. No need to worry about discrete structures to differentiate between arrays, dictionaries or structs. Even classes are "just" tables with a metatable. This simplicity makes it easy to learn and use, while providing enough power to do most things you need to do. For example, let's implement a simple binary search in Lua: function binary_search(array, value) local low = 1 local high = #array -- # is the length operator while low <= high do -- library functions are accessed via the global table local mid = math.floor((low + high) / 2) local mid_value = array[mid] if mid_value < value then low = mid + 1 elseif mid_value > value then high = mid - 1 else return mid end end return nil end res = binary_search({2, 4, 6, 8, 9}, 6) print(res) All this should be relatively familiar to look at, even if you've never seen Lua before. The only thing that might be unfamiliar is the local keyword, which is used to declare variables. Variables are global by default, so local is used to declare a variable as local to the current scope. Compilability Lua is an excellent target to compile to, due to its simplicity and easy C interop. This makes it a great choice for domain-specific languages (DSLs), such as Terra, MoonScript and Fennel. As a quick example, here is that same binary search implemented in MoonScript and Fennel: binary_search = (array, value) -> low = 1 high = #array while low <= high mid = math.floor((low + high) / 2) mid_value = array[mid] if mid_value < value low = mid + 1 else if mid_value > value high = mid - 1 else return mid return nil print binary_search {2, 4, 6, 8, 9}, 6 (fn binary-search [array value] (var low 1) (var high (length array)) (var ret nil) (while (<= low high) (local mid (math.floor (/ (+ low high) 2))) (local mid-value (. array mid)) (if (< mid-value value) (set low (+ mid 1)) (> mid-value value) (set high (- mid 1)) (do (set ret mid) (set low high)))) ; no early returns in Fennel ret) (local res (binary-search [2 4 6 8 9] 6)) (print res) Embeddability Really, the true strength of Lua is that you can embed it almost anywhere - Lua is implemented as a library for a host program, like Redis. Traditionally, this was a C program, but there are implementations of a Lua VM in many languages, such as JavaScript via Fengari or Go via GopherLua. However, it has perhaps achieved most success as the primary scripting language for Roblox. Perhaps one of my favourite uses is through HAProxy, harkening back to the days of Apache + mod_php scripting. Let's set up a HAProxy configuration to respond to requests on a particular path with a random fortune: local function fortune(applet) local responses = { { quote = "The only people who never fail are those who never try.", author = "Ilka Chase" }, { quote = "The mind that is anxious about future events is miserable.", author = "Seneca" }, { quote = "A leader is a dealer in hope.", author = "Napoleon Bonaparte" }, { quote = "Do not wait to strike until the iron is hot; but make it hot by striking.", author = "William B. Sprague" }, { quote = "You have power over your mind - not outside events. Realize this, and you will find strength.", author = "Marcus Aurelius" } } local response = responses[math.random(#responses)] local resp = string.format([[
%s
--%s
Do not wait to strike until the iron is hot; but make it hot by striking.
--William B. Sprague