25. Modules and require — Homework solutions

Each problem has its own folder under exercises/25/homework/solutions/. To run one:

cd exercises/25/homework/solutions/01-greet-module
lua main.lua

Problem 1 — Greet module

Problem. Finish a greet.lua module exposing hello and bye.

How to think about it. Standard pattern: create M, add functions, return M. Both are tiny — one print each.

Worked solution. greet.lua:

local M = {}

function M.hello(name)
    print("Hello, " .. name .. "!")
end

function M.bye(name)
    print("Goodbye, " .. name .. ".")
end

return M

main.lua:

local greet = require("greet")

greet.hello("Keiko")
greet.bye("Keiko")

Common mistakes.

  • Forgetting return M at the end. require then returns true, not the table, and greet.hello fails with attempt to index a boolean value.
  • Running from the wrong directory. cd into the folder first.

Problem 2 — Math helpers

Problem. A module with double and triple.

How to think about it. Same shape as problem 1, but with maths instead of prints. M.double takes a value and returns a new value — the caller does the printing.

Worked solution. math_helpers.lua:

local M = {}

function M.double(x)
    return x * 2
end

function M.triple(x)
    return x * 3
end

return M

main.lua:

local mh = require("math_helpers")

print(mh.double(7))    -- 14
print(mh.triple(7))    -- 21
print(mh.double(mh.triple(5)))   -- 30

Common mistakes.

  • print inside the module. Returning values lets the caller decide what to do with them.

Problem 3 — Counter

Problem. A module with increment, get, and reset. The internal counter must persist across calls.

How to think about it. A local at the top of the file (outside any function) lives for the module's lifetime — which, thanks to require's caching, is the whole program's lifetime. All three exposed functions read and modify that same variable.

Worked solution. counter.lua:

local M = {}

local count = 0

function M.increment()
    count = count + 1
end

function M.get()
    return count
end

function M.reset()
    count = 0
end

return M

main.lua:

local counter = require("counter")

counter.increment()
counter.increment()
counter.increment()
print(counter.get())     -- 3

counter.reset()
print(counter.get())     -- 0

Common mistakes.

  • Declaring count inside M.increment. Every call resets it to 0, then bumps it to 1. Persistent state must live outside the function.
  • Putting count on M (i.e. M.count). That works, but lets every other file overwrite count directly. A module should expose only a controlled surface.

Challenge — String utilities

Problem. A module with three string functions of your own design.

How to think about it. Pick three. The example shows shout, echo, and reverse_words. The third splits on spaces by walking the string; if that feels too much, swap in something simpler, like M.exclaim(s) -> s .. "!".

Worked solution. string_utils.lua:

local M = {}

function M.shout(s)
    return string.upper(s) .. "!"
end

function M.echo(s, n)
    local parts = {}
    for i = 1, n do
        parts[i] = s
    end
    return table.concat(parts, " ")
end

function M.reverse_words(s)
    local words = {}
    for word in string.gmatch(s, "%S+") do
        table.insert(words, 1, word)
    end
    return table.concat(words, " ")
end

return M

main.lua:

local su = require("string_utils")

print(su.shout("hello"))             -- HELLO!
print(su.echo("hi", 4))              -- hi hi hi hi
print(su.reverse_words("one two three four"))  -- four three two one

string.gmatch is a built-in that walks every match of a pattern in a string. %S+ means one or more non-whitespace characters — a word. table.insert(..., 1, word) puts each new word at position 1, pushing earlier ones along, so the list ends up reversed.

Common mistakes.

  • Forgetting that string.gmatch is fine to use even though it has not had its own section. Every built-in function is available the moment Lua starts.

Done?

Part 5 is finished once the four homework programs run. The Part 5 mini-project — a Text Adventure — combines functions, tables, and modules into a tiny game world.