In discussions around AI-assisted programming with LLMs, I often see two opposing camps. And since the release of Astra, it's become even more obvious...
On one hand, there's the idea that code is becoming just an implementation detail: you write the specs, add some tests, and let the models do the heavy lifting.
On the other hand, some argue we still need to scrutinize every single line, or even avoid these tools entirely to keep true mastery over our software.
I might be forcing the trait a bit, sure. But not by much. Just scroll through tech X or LinkedIn, and you'll inevitably run into these two takes in the comment sections.
This tension raises an important question:
What shape should our code take so that we can confidently delegate more of the writing process, while still actually understanding it?
Basically: can we split the difference? Can we find a compromise that pulls everyone upward?
Because, as is often the case in life, the most useful path is found in hybridization, in dialectics: a successful marriage of both worlds.
This is where PureScript seems to offer something special. (But it is not the only one. Quick reminder BTW: PureScript is a pure, statically typed functional language, close to Haskell).
What interests me here is the ability to write a program whose structure stays closely aligned with actual business reasoning.
PureScript code remains an implementation, but it naturally carries a chunk of the specification itself. That's exactly what your specs look like when you realize you need to brush up on technical terminology to make sure your AI understand you clearly. That's the marriage we're looking for.
See what I mean? When you're concatenating strings... what you care about is the text. You don't want to worry about how to manage memory safely, or what differentiates a Chinese character from an English one at the byte level.
Don't get me wrong, I'm not saying those low-level details are uninteresting. I'm saying that asking yourself those exact same questions every single day is much less interesting.
So, the best approach is to solve the problem once, and then let a program translate your high-level ideas based on those solutions. There's a word for that: a compiler.
And yes, just as a compiler turns your C or Rust into low-level machine instructions, a program can now translate your high-level technical ideas into C or Rust code. We call that a transpiler because it sounds more modern, but the philosophy is exactly the same.
Let's take a refund decision as an example:
data RefundDecision
= Approve Amount
| Reject RejectionReason
decideRefund :: RefundPolicy -> Order -> RefundDecision
Even without knowing the syntax, you can immediately read a few things. The decision depends on a refund policy and an order. It can approve an amount, reject with a reason, or require a manual review with a reason.
These possibilities are explicitly present in the program's model. They can be discussed before anyone even looks at the concrete algorithm used to implement them.
And it gives humans something reasonable to review. It's as concise as a spec document. What extra noise is there? A data, an =, a |. That's it! Not much more than the commas and periods that structure standard human writing.
After that, you feed this PureScript code into a smart compiler that knows exactly what to do to squeeze out maximum performance, and you get something like that (e.g, in C++):
struct RefundDecision {
enum { TAG_APPROVE, TAG_REJECT } tag;
union {
double approveAmount;
struct RejectionReason* rejectReason;
} _data;
};
inline struct RefundDecision* Core_decideRefund(
const struct RefundPolicy* policy,
const struct Order* order
) {
// ... Boilerplate memory management, bit-twiddling,
// and auto-generated C++ optimizations ...
return nullptr;
}
Fast generation is NOT fast comprehension
If a modification adds a ton of code, you still have to re-discover the underlying assumptions, track state changes, and understand the interactions. The fact that this code was generated in seconds doesn't magically eliminate that cognitive effort.
When you crank out code in 5 minutes with an AI, it's awesome. The payoff is immediate, and you want to go tell everyone about your miracles. And you're absolutely right to.
Except?
Except there's a slight problem... one day, that code is going to need a change. Too complicated to touch? You'll just ask the AI again. And there you have it: the loop is closed. You depend entirely on AI to code, it costs money, and you have nothing but your specs & tests to save you.
But you know as well as I do that tests aren't enough. At some point, you actually have to play around with the code.
You wouldn't board a plane if they told you its software had never been read by a human.
(Okay, maybe 1% of you would. I'm talking to the remaining 99%.)
This is why the best approach is ultimately to return to a middle ground, rather than betting the house entirely on "all-spec" and "all-test". (Or use a proven language this time, like Lean.)
What you really need is a concise, spec-oriented language.
With small functions, explicit data structures, and strictly bounded side effects, you can focus your code reviews on actual decisions:
- Do the represented states correspond to the business domain?
- Is the business rule correct?
- Are edge cases handled?
- Do side effects occur at the right time?
Types take care of a good chunk of the consistency checks. They also provide the LLM with hard constraints that its proposed output must satisfy.
Obviously, this doesn't make a program magically correct.
A function might apply a 30-day refund window when the rule states 14 days. It will still type-check perfectly. Tests remain necessary to verify examples, properties, and interactions. Code review remains necessary to examine assumptions.
This is exactly why PureScript interests me: because it brings together everything we care about today.
The specification naturally extends into the types. The rules extend into pure functions. Tests can directly exercise these functions without needing to spin up an entire runtime environment.
When you write PureScript, you don't need side specs. The code IS your spec document. You compile it, and that's it. (And it's deterministic.)
Catch you later, folks!
By the way, after Go and PHP, my latest very serious hobby is compiling to Rust. I'm currently building a PureScript-to-Rust compiler, and the performance is identical to, or even better than, hand-written Rust code (because a human won't bother rethinking every possible optimization step...).
Of course, the entire ecosystem (the crates) will still be free to use (thanks to FFI). As I said in previous articles: PureScript is not an ivory tower, and you can leverage existing libraries.
(It's a WIP, I'll talk about it again when it's finished. You can find it here.)
Top comments (1)
@zelenya If you read the article, and just so you know: I made huge improvements on the Rust compiler :)