Haskell Made Me a Better AI Engineer
I spent a session this week building a Todo app in Haskell.
Not because I needed a Todo app. Because I needed to understand what it feels like to write code that cannot lie.
That's not a metaphor. In Haskell, the type system enforces it. A function that might fail returns a Maybe. A function that might fail with information returns an Either. You can't call something and pretend the failure case doesn't exist. The compiler won't let you. The failure is written into the contract of the function itself, visible to anyone who reads the signature.
I've been doing QA and systems assurance for 38 years. I've watched teams ship software that silently swallowed errors. I've written incident reports about systems that returned results confidently when they should have returned nothing. I've sat in reviews where the question "what happens when this fails?" was met with a blank look.
Haskell made me realise how much of that is a language design problem — not just a discipline problem.
The type signature is the spec
In most languages, you write a function and the name tells you what it does. In Haskell, the type signature tells you what it actually does.
Here's a simple example. A function that looks up a user by ID:
lookupUser :: UserId -> Maybe User
That Maybe isn't decoration. It's a machine-readable guarantee. This function either returns a User or it returns Nothing. The calling code must handle both cases. There's no null pointer waiting to surprise you in production. There's no silent return of an empty object that looks like a real one.
Now think about AI systems.
Most inference pipelines don't have this. They take an input, they produce an output, and the contract in between is a black box. The model was trained on a distribution. If your input sits outside that distribution, you might still get a confident-looking result. You'll get a User back when you should have gotten Nothing.
That's not a Haskell problem. That's an accountability problem. But Haskell showed me the shape of it.
Total functions and the AI parallel
In Haskell, the goal is to write total functions — functions that are defined for every possible input. No crashes. No undefined behaviour. If you can't handle an input, the type system forces you to be explicit about that.
The opposite is a partial function: one that blows up on certain inputs, or silently returns nonsense.
The most dangerous AI systems I've reviewed are partial functions with no type signatures.
They work beautifully on the training distribution. Demo perfectly. Pass benchmarks. Then they meet an edge case in production — a jurisdiction they weren't trained on, a document format they haven't seen, a customer query phrased in a way the fine-tuning didn't cover — and they return something. Confidently. Without flagging that they've left the safe zone.
No Nothing. No Left "I don't know". Just output.
That's a partial function. And in regulated industries — financial services, healthcare, defence — partial functions with confident outputs are a governance catastrophe waiting to happen.
Explainable is not enough
There's a distinction I keep having to make with clients. They show me their AI system. They point to the dashboard that explains why the model made a decision. They say: we're covered.
They're not covered. They're one-third covered.
Explainable is a property of the model. It can show its working. Here's the feature weighting, here's why this output. It's a single moment. It tells you what the system did. It doesn't tell you whether the system should have done anything at all.
Auditable is different. It's a documented record — test results, validation logs, change history, exception reports — that can be independently reviewed. It proves the system behaved as claimed, not just once but across conditions, across time, across edge cases.
Accountable means a named person owns the outcome. Not the algorithm, not the vendor, not "the team." A specific human being who stands in front of a regulator and answers for it.
Haskell's type system is a form of enforced auditability. The contract is in the code. Anyone can read it. The compiler verifies it on every build. You can't merge a change that breaks the contract without explicitly acknowledging the breakage.
We don't have that for AI systems yet. We're still in the era of verbal assurances and post-hoc explanations.
What I'm actually arguing for
I'm not saying every AI team should learn Haskell. I'm saying the habits Haskell builds are the habits AI governance needs.
Make failure explicit. Name the cases where the system shouldn't return a confident answer. Build the Nothing path and the Left path as first-class citizens — not afterthoughts. Write the contract before you write the code. Treat a function that silently degrades as a defect, not a feature.
The UK AI regulatory conversation is circling this. The FCA's guidance on model risk, the emerging discussion around algorithmic accountability under SMCR — these are attempts to impose type-system discipline on systems that currently have no signatures.
The profession that enforces this — let's call it AI assurance — doesn't formally exist yet.
It will. The question is whether the people who define it will have thought carefully about what failure modes actually look like.
Haskell helped me think about that. A Todo app with total functions.
Not the worst way to spend a morning.
Mike Lee is an AI implementation advisor with 38 years of experience across 16 industries. He writes about AI governance, accountability, and the infrastructure of trust at Monospace.
© Q2 2026