Shortest Hello World to Literate Programming
Shortest hello world
In almost every programming language, greeting the world requires some ceremonies. One early example from 1974 Bell Laboratories shows how to do it in C, which requires printf and main:
main( ) {
printf("hello, world");
}In a document-oriented programming language (like Tex, Typst, Scribble, Literate Haskell), the hello world program is literally just 12 characters:
hello, worldThis is arguably the world's shortest hello world program.
Saying it is a program, means that it can compute, unlike a Markdown or TXT file. For example:
hello, #upper("world") // Typst
hello, @(string-upcase "world") // Scribble
hello, \(string.upper "world") // KikiThen the program is evaluated to "hello, WORLD".
Invert code and block comment
Modern programming languages often adopt a markup format in their block comment systems. For example, Rust supports:
- Doc level comments to explain high level architectures.
- Markdown directly in block comments, such as bold and lists.
- A custom syntax to link symbols by name.
These are quite useful when reading documentation, allowing non-linear exploration.
What if we take a step further and make comments first class?
Let's see a piece of Kiki program:
`fib` returns n-th Fibonacci number. E.g., 10th Fibonacci number is \(fib 10).
> fib = fn 0 -> 1
fn 1 -> 1
fn n -> fib (n - 1) + fib (n - 2)Note a few interesting things:
- The program starts with prose (like block comments) instead of code. A leading
>enters code mode. - Backtick like
fibin prose refer to local symbols or imported symbols (such asstring.trim). In the IDE and documentation, clicking a symbol jumps to its definition. - Inline computation is written with
\(..).
The core idea is that both prose and code are parsed and type-checked by the same compiler, rather than two separate systems.
The main cost of inverting code and comments is that, now code requires additional syntax and indentation.
However, the benefits are significant too:
- We have a home for docs. Design docs and module level comments all use the same markup syntax.
- Docs can keep sync with code. Refactor can rename a symbol across all code and prose references.
- Context is always nearby. Nowadays AI is pretty good at locating context, though easily available context still helps.
Literate programming
Donald Knuth introduced Literate Programming in 1984 when he built the Tex typesetting system. He created a system called WEB, which embeds Pascal code in the Tex prose. The whole Tex implementation is a 25101 lines of tex.web file. It is like a book or a design doc, started with high level designs, then progressively introduce algorithms used and code pieces. The file can be extracted into Pascal program and Tex doc. Tex being impelemented by Tex+Pascal is an interesting bootstraping approach.
a
asdfa
Living examples over comment rot
When Donald Knuth introduced Literate Programming in 1984, he formulated its core philosophy:
The main idea is to regard a program as a communication to human beings rather than as a set of instructions to a computer.
In traditional software development, comments and code live in separate worlds:
- Code is parsed, type-checked, and executed by the machine.
- Comments are ignored strings of text.
Because the compiler ignores comments, they inevitably rot. We have all encountered docstrings with outdated return values, broken code snippets, or explanations for arguments removed three refactors ago.
When prose is first-class, \(fib 10) shares the same scope and runtime as the fib definition. If fib changes or breaks, the prose immediately updates or raises a compile error. The example is not a static snapshot—it is a living evaluation. The explanation cannot drift from the execution.
The friction of the past
If the idea is so compelling, why didn't literate programming become the default way we write all software?
Historically, the friction came from the toolchain:
- **Order of explanation vs. order of compilation**: Early languages like Pascal and C required strict file structures (globals declared first, forward declarations, header files). To let humans explain ideas in a natural storytelling order, early literate systems had to scramble and reassemble code chunks through a separate extraction pass before feeding them to the compiler.
- **Separate typesetting passes**: Before modern rich-text editors and browsers, reading formatted code required a dedicated preprocessing step to generate TeX documents for printing.
This split created friction: compiler errors pointed to generated files rather than the original document, and standard debuggers and IDEs struggled to keep up.
In a modern expression-oriented language, those barriers disappear. Functions, expressions, and prose can coexist in a single unified AST. There is no preprocessor or code extraction pass—the document is the source, and the source is the document.
First-class explanation in the tooling
In most editors, comments are styled in a muted gray color—a subtle cue that the editor and compiler do not care about what is written there.
When prose is part of the language grammar rather than discarded in the lexer:
- **Symbol navigation**: Writing
fibis not inert text; it is an AST identifier node. Hovering shows its type, clicking jumps to its definition, and refactoring renames it across both code and text. - **Unified diagnostics**: Type errors in inline expressions like
\(fib "abc")appear directly in the editor alongside regular code errors.
Code as an essay vs. code as plumbing
The goal of a document-first style is not to force every five-line utility function or configuration file into an essay. Much of software engineering is plumbing where concise, traditional code files remain effective.
Rather, literate programming shines where human intent matters most: algorithms, tutorials, technical design specifications, domain models, and data analyses. In these domains, understanding *why* a piece of code exists is just as crucial as *what* instructions the CPU executes.