The best software I’ve written shares something with good mathematics. Not because it uses advanced math, but because it embodies the same principles: abstraction, composition, and invariants.
What I Mean by Mathematical Structure
In mathematics, good work has certain properties:
Generality: One theorem covering many specific cases. That reveals structure.
Composability: Small results combine to prove larger results.
Invariants: Properties preserved across transformations.
Minimal assumptions: Maximum results from minimum prerequisites.
Inevitability: Each step follows naturally from the previous.
These same properties make software better.
Generality in Code
Good abstractions are general.
Instead of:
def sum_integers(lst): ...
def sum_floats(lst): ...
def sum_complex(lst): ...
Write:
def sum(iterable): ... # Works for any numeric type
This isn’t just code reuse. It’s recognizing essential structure independent of representation.
Mathematical parallel: group theory doesn’t care if you’re rotating shapes or permuting elements. It cares about the structure of symmetry itself.
Composability
Mathematical proofs compose: lemmas build into theorems build into theories.
Software should work the same:
def normalize(vector):
return vector / magnitude(vector)
def magnitude(vector):
return sqrt(sum(x**2 for x in vector))
# Compose naturally
unit_vector = normalize(data)
Each function does one thing. They combine predictably.
This is Unix philosophy through a mathematical lens.
Invariants as Correctness
In math, invariants let you reason about properties that survive transformations.
In code, invariants are contracts that must hold:
- A sorted list stays sorted under these operations
- This hash function is deterministic
- This pure function returns same output for same input
- This encryption preserves confidentiality
Design APIs to make important invariants obvious and enforceable.
Type systems help:
-- Once encrypted, type system won't let you use as plaintext
encrypt :: PlainText -> Key -> CipherText
decrypt :: CipherText -> Key -> PlainText
Minimal Assumptions
Mathematical theorems state assumptions explicitly: “Let X be a metric space with property P…”
Software should too:
Type signatures declare valid inputs:
def cosine_similarity(v1: np.ndarray, v2: np.ndarray) -> float:
"""
Precondition: v1 and v2 are non-zero vectors of same dimension
Returns: similarity in [-1, 1]
"""
Documentation states what must be true:
- Preconditions: what the caller must ensure
- Postconditions: what the function guarantees
- Invariants: what always holds
Make it hard to violate assumptions accidentally.
Inevitability in Design
The best mathematical proofs feel inevitable. Each step follows naturally.
The best software feels the same:
Function names that make usage obvious:
# Not: process_data(x, True, False, 3)
# But:
results = data.filter(predicate) \
.map(transform) \
.reduce(aggregator)
Type systems that guide toward correct code:
// Rust's ownership system makes it hard to write memory-unsafe code
// The correct way is the natural way
APIs where the right way is the natural way:
# Natural usage:
with open(filename) as f:
data = f.read() # Automatically closes, handles errors
When users think “of course, what else would it be?” you’ve got it.
Two Degrees, One Design Sense
My two master’s degrees gave me complementary modes of thinking:
Computer Science (2015): How to build systems that work
- Algorithms and data structures
- Systems programming
- Distributed computing
- Practical engineering
Mathematics (2023): How to reason about structure and correctness
- Rigorous proofs
- Abstract algebra
- Measure theory
- Mathematical maturity
The combination matters. You can build systems with mathematical structure, and that structure pays off.
Examples From My Work
Statistical libraries:
Functions compose like mathematical operations:
bootstrap(estimator, data, resamples = 1000) %>%
confidence_interval(level = 0.95)
Returns a distribution you can query. Each piece does one thing. They compose naturally.
Cryptographic tools:
Invariants enforced by types:
// Encrypted<T> can't accidentally be used as T
// Type system prevents confusion
template<typename T>
class Encrypted { ... };
Network analysis:
Abstract over graph representations:
def pagerank(graph: GraphProtocol, damping=0.85):
"""Works on any graph satisfying the protocol"""
Write algorithms that work on any structure satisfying the interface.
Why This Matters
Mathematical structure in code isn’t aesthetic preference. It’s engineering for comprehension.
Code with this structure is:
Easier to reason about: Invariants guide understanding
Easier to test: Composability enables isolated testing
Easier to extend: Generality means fewer special cases
Easier to prove correct: Invariants support formal reasoning
Easier to maintain: Clear structure resists decay
The Discipline
Achieving this requires discipline:
Resist special cases: Factor out common structure instead
Name things precisely: Names should reveal intent
State assumptions clearly: Document preconditions and invariants
Make properties obvious: Important characteristics should be explicit
Compose small pieces: Build complex from simple
It’s harder than throwing code together. But the result is software that lasts.
Practical Implications
For my research:
Reliability analysis code: Estimators defined abstractly, work for any censoring pattern
Network analysis tools: Graph algorithms independent of representation
Statistical methods: Mathematical properties preserved in implementation
For general development:
Write functions that compose: Small, focused, predictable
Use types to encode invariants: Let the compiler help maintain correctness
Document the mathematics: Explain theoretical properties of your code
Test invariants explicitly: Verify properties that should always hold
The Legacy Question
With stage 4 cancer, I think about code differently.
Will someone else understand this design? Are the mathematical principles clear? Can this be extended without me? Does the structure communicate intent?
Mathematical structure helps here. Code structured like a proof, stating its assumptions, proceeding logically, reaching inevitable conclusions, verifiable independently, is more continuable than clever hacks.
Recognizing Structure
Monoids everywhere:
# Recognizing monoidal structure
# Identity element, associative operation
[].extend([1,2]).extend([3,4]) # List concatenation
0 + 5 + 10 # Addition
1 * 5 * 10 # Multiplication
"" + "hello" + "world" # String concatenation
Functors in practice:
# map preserves structure
list.map(f).map(g) == list.map(lambda x: g(f(x)))
Option.map(f).map(g) == Option.map(lambda x: g(f(x)))
Type algebra:
// Sum types: A | B (union)
// Product types: {a: A, b: B} (record)
// Function types: A => B
// These compose algebraically
Recognizing these patterns makes code comprehensible at a higher level.
What I’m Aiming For
Every library I publish aims for:
Mathematical clarity: Structure reflects underlying theory
Compositional design: Small pieces combine naturally
Explicit invariants: Important properties are obvious
Minimal assumptions: Work with maximum generality
Inevitable usage: The right way feels natural
This takes time. But it’s how you build things that last.
The Bottom Line
CS taught me how to build. Math taught me how to reason about what I build.
The combination:
- Code that composes
- Types that enforce correctness
- Abstractions that reveal structure
- Invariants that guide reasoning
- Designs that feel inevitable
Cancer doesn’t change this. If anything, it sharpens it: do work that’s comprehensible, continuable, and correct.
Mathematical structure serves all three.
Discussion