Table of Contents
- 1. The Problem at a Glance (Advent of Code 2025 – Day 1)
- 2. Running the Examples
- 3. What This Example Intentionally Does NOT Cover
- 4. Comparing the Code: Python vs. Mojo 🔥
- 5. What Mojo Adds (in This Small Example)
- 6. Summary
- Appendix A – Summary Python vs. Mojo Comparison Table
- Appendix B – Full Code Listings at the Mojo Fireplace
Mojo is a new programming language from Modular that is deliberately designed as a superset of Python. It keeps the syntax Python developers already love: indentation, familiar control flow, even with open(...) while adding static typing, ownership rules, and systems-level performance features. The promise is simple: write code that feels like Python, but runs much faster when you need it to.
Released in preview in 2023, Mojo is already a production-ready language actively developed by Modular for high-performance AI and systems programming, with a stable release in November 2025 (v0.25.7). Its standard library is open-source under Apache 2.0, and the full compiler is planned for open-sourcing in 2026 as the language matures, backed by a growing community of over 6,000 contributors.
We’ll take a straightforward Python solution and transform it to Mojo while keeping the overall shape as close as possible. The goal is not to write the most idiomatic Mojo possible, but to let you see exactly what changes (and what stays comfortingly the same).
In this post I will:
- Start from a small, CPU-only Python script that solves the puzzle.
- Transform it to Mojo in a way that keeps the structure as similar as possible.
- Highlight the key differences and what they mean for a Python programmer.
- Finish with a summary table and full code listings.
1. The Problem at a Glance (Advent of Code 2025 – Day 1)
We use a simple real-world example: the puzzle from Day 1 of Advent of Code 2025. It’s a perfect first-day problem, small, CPU-only, heavy on basic I/O and loops, which makes it ideal for comparing the two languages without distractions.

Arriving at the North Pole, you are confronted with safe to open. It has a dial with 100 numbered positions (0–99) initially pointing at position 50. Your puzzle input is a list of rotations in the format L<number> (rotate number positions left) or R<number> (rotate number positions right).
Your task is to determine the password via:
- Apply every rotation in order, wrapping around using modular arithmetic.
- Count how many times the dial lands exactly on position 0 after a move.
- The resulting count is the “password” that unlocks the door.
2. Running the Examples
You will have two implementations that behave identically:
- Python (with types) →
day1_typed.py(with standardtypinghints). Seeday1.pyfor a type-free version of the code. - Mojo →
day1.mojo(with mandatory, compiler-enforced types)
Both expect your personal input in day1_input.txt (provided individually by user on the AoC site) or the small test file (reproduced above) in day1_input_test.txt.
# Python (optional runtime typing, enforced only by tools like mypy/pyright)
uv run day1_typed.py # or just: python day1_typed.py
# Mojo (types are required and checked by the compiler)
mojo day1.mojo
Both print the number of rotations, min/max rotation size, and finally the password.
Note: To run the Mojo version you need the Mojo SDK. The official guide is here: https://docs.modular.com/mojo/manual/get-started/. If you’re already in a Python project, the quickest way is:
uv add modular
This installs the latest stable Mojo toolchain into your uv-managed environment and makes mojo available immediately — no system-wide setup required.
3. What This Example Intentionally Does NOT Cover
A quick heads-up so expectations are clear:
- No performance optimisations – The puzzle is tiny; both versions are kept deliberately simple and structurally identical.
- No benchmarking – Mojo shines on larger workloads. Modular’s site has great benchmarks, and I’m preparing follow-up posts that dive into real performance comparisons.
- No advanced Mojo features – No SIMD, structs, memory views, parameterisation, or parallelisation. Those would hide the gentle Python-like feel being showcased.
- No extra error handling – Let I/O and parsing errors raise naturally, exactly like a typical small Python script would.
Everything here is intentionally minimal and side-by-side.
4. Comparing the Code: Python vs. Mojo 🔥
Step 1 – Constants and Configuration
Python (with types) (day1_typed.py)
INITIAL_POSITION: int = 50
N_POSITION: int = 100
INPUT_FILE: str = "day1_input.txt"
INPUT_TEST_FILE: str = "day1_input_test.txt"
Mojo
comptime INITIAL_POSITION: Int = 50
comptime N_POSITION: Int = 100
comptime INPUT_FILE: String = "day1_input.txt"
comptime INPUT_TEST_FILE: String = "day1_input_test.txt"
Same – module-level constants.
Different – Mojo uses compile-time comptime with explicit types.
Step 2 – Reading Rotations from a File
Python (with types) (day1_typed.py)
from typing import List
def load_rotations(filename: str) -> List[int]:
rotations: List[int] = []
with open(filename, "r") as file:
for line in file:
line = line.strip()
if not line:
continue
if line.startswith("L"):
rotations.append(-int(line[1:]))
else:
rotations.append(int(line[1:]))
return rotations
Mojo
fn load_rotations(filename: String) raises -> List[Int]:
var rotations = List[Int]()
with open(filename, "r") as file:
var lines = file.read().split("\n")
for line in lines:
var line_stripped = line.strip()
if len(line_stripped) == 0:
continue
if line_stripped.startswith("L"):
rotations.append(-Int(line_stripped[1:]))
else:
rotations.append(Int(line_stripped[1:]))
return rotations.copy()
Key Mojo takeaways: explicit types, raises, and the required .copy() due to ownership rules.
A small but important syntactic note: in Mojo, fn defines a fully compiled Mojo function. There is also def syntax for Python-compatible definitions, but in performance-sensitive or fully Mojo code you will generally prefer fn, as it participates in Mojo's type system and ownership model in a way that is more like a "strict" version of Python's def with static checks.
Step 3 – Updating the Dial Position
Python (with types) (day1_typed.py)
def update_position(current_position: int, rotation: int, n_position: int) -> int:
return (current_position + rotation) % n_position
Mojo
fn update_position(
current_position: Int, rotation: Int, n_position: Int
) -> Int:
return (current_position + rotation) % n_position
The algorithm is 100% identical — only type annotations differ.
Step 4 – Main Loop and Counting Zeros
Here’s a lightly-trimmed view of the main loop in the typed Python version:
# pick input file based on a debug flag
input_file: str = INPUT_TEST_FILE if DEBUG else INPUT_FILE
rotations: List[int] = load_rotations(input_file)
current_position: int = INITIAL_POSITION
count_zeros: int = 0
for rotation in rotations:
current_position = update_position(current_position, rotation, N_POSITION)
if current_position == 0:
count_zeros += 1
And the equivalent logic in Mojo:
var input_file = INPUT_TEST_FILE if debug else INPUT_FILE
var rotations = load_rotations(input_file)
var current_position = INITIAL_POSITION
var count_zeros = 0
for rotation in rotations:
current_position = update_position(current_position, rotation, N_POSITION)
if current_position == 0:
count_zeros += 1
Both versions choose the input file, load rotations, iterate, update the position, and count zeros in lockstep. Control flow and variable names are essentially the same; the visible differences are printing style and the local debug flag. For the full main functions (including debug prints and summary output), see the repository files referenced in Appendix B.
5. What Mojo Adds (in This Small Example)
- Static typing that is enforced by the compiler, not just hinted.
- Explicit error handling via
raises. - Ownership & copying — a gentle introduction to a powerful concept.
- Same algorithm, same shape — no logic rewrite required.
- A stricter type system than Python’s optional hints: there is no “untyped” Mojo in the way there is untyped Python (even when you add
typinghints).
6. Summary
For Python developers, Mojo feels like “Python with superpowers”:
- Keep your indentation, loops, context managers, and mental model.
- Add types, explicit errors, and ownership only where they help.
- Start with almost-Python and gradually turn on performance when you need it.
This tiny example is deliberately simple so the language itself stays in the spotlight.
And that’s it! A complete, real Advent of Code solution compared line-by-line from Python to Mojo — one small sleigh ride later, and you’re already fluent in Mojo ;)
Ready for another next step?
Once you've played with this Day 1 example, the best official "Hello World"-style tutorial to continue your Mojo journey is Modular's excellent Conway's Game of Life walkthrough.
Coming Soon: I'm preparing an in-depth technical series that builds on Conway's Game of Life to explore real performance gains with Mojo. We'll progressively optimise from pure Python through NumPy to multiple Mojo versions, with honest benchmarks, performance attribution, and documented failures. This will show where Mojo truly shines (and where Python+NumPy is already good enough). Stay tuned for Welcome to Mojo: From Python to Performance — launching soon!
Happy coding (in Python, Mojo, or both) — and Merry Christmas 🎄!
Appendix A – Summary Python vs. Mojo Comparison Table
| Aspect | Python | Mojo |
|---|---|---|
| Constants | Plain assignments | comptime Name: Type = value |
| Types | Dynamic at runtime; optional typing hints checked by external tools | Static and explicit; enforced by the compiler |
| Collections | Built-in list | List[Int] from stdlib |
| File I/O syntax | for line in file: | Same context manager + manual split/loop |
| Error handling | Implicit exceptions | raises on functions that can fail |
| Ownership | References everywhere | Explicit copy when returning owned data |
| Printing | f-strings | Comma-separated print + String(...) |
| Entrypoint | if __name__ == "__main__": | fn main() raises: |
Appendix B – Full Code Listings at the Mojo Fireplace
The code listed below is also available in the my companion mojo-fireplace GitHub repo.
To keep this article focused on the comparison (rather than duplicating source code), the full and always-up-to-date listings live in the repository:
- Typed Python implementation (
day1_typed.py) –src/advent_of_code/day1_typed.py - Mojo implementation (
day1.mojo) –src/advent_of_code/day1.mojo