Map
โ†‘Zig

A Haskeller's case for Zig

Wiki summaryzighaskellcomptimetype-systemlanguage-design โ†ณ show in map Markdown
title
A Haskeller's case for Zig
type
summary
summary
A long-time Haskell user evaluates Zig on three axes โ€” domain expressiveness, type-system programmability, mean-free path โ€” and concludes comptime gets you most of Haskell without GC
parent
zig
tags
zig, haskell, comptime, type-system, language-design
created
2026-04-30
updated
2026-04-30

The author of pure-systems-blog has been writing Haskell for 10+ years and evaluates new languages on three axes:

  1. How cleanly the language lets you express the program's domain (anything you write that isn't about the domain is noise โ€” manual memory management is the canonical example).
  2. What the language gives you for correct-by-construction programs and how programmable the type system is.
  3. The mean-free-path-language โ€” how many lines you can write before your understanding of the program diverges from what the program actually does.

Zig scores well on all three, and the post walks through why.

The garbage-collection critique

GC was a defensible tradeoff in 1995 when CPU and memory speeds were comparable. Since then CPU has gotten roughly 10,000ร— faster while memory access has barely budged, but languages still assume a 1995 machine. The "forest of pointers into the heap" pattern that GC encourages caps performance.

There's also a cognitive cost the author flags: GC makes it too easy not to think about the machine. A generation of developers grew up never having to. The result is software that is bloated and slow on hardware that is effectively a supercomputer compared to what we had 30 years ago. (Compare peril-of-laziness-lost โ€” Cantrill making the same charge from a different angle, that LLM coding removes the human-time constraint that used to force simplicity.)

The author cites Stephen Diehl's argument that nobody has the incentive to build a real new language: academics commit career suicide doing the engineering, industry can't fund decade-long projects, hobbyists don't have the time or money. So the field is stuck at a local maximum. Zig is interesting because Andrew Kelley's project "has the courage" to actually try.

Comptime as type-system programming

The author wants Haskell-style type-system programming without paying for GC. Zig's comptime is the mechanism:

  • newtype is just a singleton struct: struct PlayerHealth { health: u32 }.
  • Sum types are tagged unions โ€” there's even built-in optional syntax, and you can build your own Maybe(T) as a comptime function returning a union(enum) with value: T and nothing variants, with a map method that pattern-matches on the variant.
  • Typeclasses are comptime functions that take a type, check it has the required methods (if (!@hasDecl(T, "eql")) @compileError(...)), and return a struct of wrapper functions. The catch is you have to pass the resulting struct around explicitly โ€” EqPoint.eql(a, b) instead of a == b. That's the typeclass dictionary made manual. The clunkier version (you call Eq(Point) at the use site) can be cleaned up by having the type itself derive its own dictionary as a pub const Eq = EqClass(@This()) field.

The dictionary-passing being explicit is the point, not a wart. Zig's stated philosophy is "no spooky action at a distance." Implicit typeclass dispatch is exactly the kind of action-at-a-distance the language refuses to do, and the author thinks that tradeoff is worth it.

The accidental rediscovery of monads

Zig 0.16 reworked its IO subsystem to an interface design. The example in the release notes:

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;
    const io = init.io;
    var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
    ...
}

The author looks at this and sees a Reader monad carrying an allocator and an IO interface โ€” the same shape Haskell's IO and IO# have. He frames monads not as "obscure math-y thing" but as the algebraic description of imperative programming as a computational context: you implement MonadCont to get imperative semantics, LogicT for nondeterminism with backtracking. The point is that not having a built-in notion of time gives the language more expressive ceiling and a higher optimization ceiling. Zig's team apparently arrived at the IO-passing pattern independently, which the author reads as confirmation that the pattern is universal.

(I don't fully buy the framing โ€” passing an explicit io parameter is just dependency injection, and calling that "the IO monad" is a stretch unless you also have do-notation and a way to compose โ€” but the underlying observation, that explicit effect-carrying is what both designs are doing, seems right.)

Bottom line

The author's score:

  • Expressiveness: comparable to Haskell, less noise than C++ or Rust for his use cases.
  • Type-system programming: comptime is "at least in the ballpark of Haskell and easier to program at."
  • Mean-free path: longer than Rust, much longer than C++. He hasn't been surprised by Zig semantics so far.

He's been a Haskell user for 10โ€“13 years and is now redirecting energy to Zig.