Modules and ecosystem
Goal: organize code in the bin/lib model, import with
use, understand the stability tiers, and get to know the official libs and themkCLI.
Module = lib; project = bin
Section titled “Module = lib; project = bin”Two markers, with distinct roles:
mk.modmarks a module, and a module is a lib: an independent, reusable box, with its own dependencies. It works as a namespace and dependency boundary. It is never compiled alone, only as part of a project’s tree (unused modules coexist at no cost).mk.projectmarks the bin (the composer): the top module and the unit of compilation. It composes the modules into a tree, declares the language version, its deps, and the tasks/binary production.
myapp/ ← mk.project (BIN: composes modules, tasks, deps)├── mk.project├── mk.sum (lockfile, auto-generated)├── main.mko → module "myapp"├── auth/ ← mk.mod (LIB "myapp/auth", its own deps)│ └── tokens.mko└── store/ ← mk.mod (LIB "myapp/store") └── db.mkoThe manifest declares the language version and the deps (like go 1.21):
package myappmakoto 1.0
deps { github.com/acme/http 2.3.1}Importing: use
Section titled “Importing: use”use json // mounts the module → json.parse(...)use json.{parse, decode} // lifts names → parse(...) directlyuse json as j // aliasWhere each use comes from is decided by the prefix: bare is stdlib (use io); a package name is local
(use myapp/auth); URL form is external (use github.com/acme/http). Imports are absolute from
the package name, never ../. Cycles are forbidden.
The stability tiers
Section titled “The stability tiers”The ecosystem is organized by how freely the contract can be changed:
- Tier 0, the core (the language plus the stdlib): standards that don’t change (IEEE, TCP/IP, UTF-8, linked
list). It is add-only: you add, you never edit the contract.
collections(List/Map/Set) is tier 0, a closed set (there will never be a “fourth fundamental collection”). - Tier 1, the official libs: specifications that evolve (HTTP had 3 versions; regex swaps
algorithms). It has its own cadence (semver/changelog), is opt-in, and costs zero weight until the
use.containersis tier 1, a roster that grows (each algorithm inside is frozen, but the collection changes). - Tier 2, the frameworks (
web, the UI): more opinion, more churn. - Tier 3, the third parties: the compatibility promise is someone else’s.
The rule is that tiers only depend downward. The cut sometimes splits a concept in half: UTF-8 is tier 0, the Unicode tables are tier 1; UTC/offset is tier 0, the IANA timezone names are tier 1.
The three official libs (tier 1)
Section titled “The three official libs (tier 1)”http: client and server, on top ofnet’s layer 2 (bytes + TLS). It has a version-agnostic surface (/1.1, /2, /3 negotiated by ALPN), process-per-stream (onepanicin a request crashes only it), and a full client (pool/redirect/cookies/gzip). The framework (router/middleware) isweb(tier 2).
use httpclient := http.Client.new()resp := client.get("https://api.example/users") catch |e| { return e }if resp.status.is_success() { process(resp.body.to_string() catch |e| { return e }) }regex: multi-engine by construction.regex.compileuses RE2 (linear, no ReDoS, the default),regex.pcre.compileuses backtracking (full features), andregex.hyperscandoes multi-pattern plus streaming. A literal pattern compiles at comptime.
const WORD := regex.compile("[a-z_][a-z0-9_]*").or_panic() // comptime: validates + embedscontainers:Deque,PriorityQueue,SortedMap/SortedSet(B-tree),Trie(ART),RingBuffer. It follows the conventionnew()(natural order) /with_order(cmp)(custom):
use containerspq := containers.PriorityQueue[Task].with_order(by_priority) // Task has no natural order → with_orderThe mk CLI
Section titled “The mk CLI”mk run / mk build / mk test / mk fmt / mk get / mk doc / mk lint / mk bench, and more
(chapter 15). A single binary, an alias for makoto.
Next: 13 · FFI and low level