I write code and I read code that other people (and models) write. The thing that eats my day is not typing — it's answering what can this code actually touch, and how sure am I? Jacquard is the first language I've seen that puts the answer on line one and forces the compiler to keep it honest.
Most languages tell you what a function computes. Very few tell you what it does to the world. In an era where a growing share of the code I review was written by a model I don't know, that gap is where the day goes: chasing lookup_alias two files deep to find out whether the innocuous-looking normalize_name reaches the network.
Jacquard is a small research language (v0.1 rc3 at the time of writing) that takes the opposite bet. Effects — network, files, clock, randomness — live in the type. The checker refuses signatures that hide them. The runtime refuses effects the CLI didn't grant. This post is my first-hand walk through the parts I actually ran.
The five-minute install
The install script drops a single binary and a demo tree. No OCaml, no opam.
curl -fsSL https://raw.githubusercontent.com/jbwinters/jacquard-lang/jacquard-core-0.1-rc3/scripts/install.sh | sh
~/.local/bin/jac run ~/.local/share/jacquard/demos/basics/m1-fact.jac
# 120
If that prints 120, the language is on your machine and you can follow along.
Hello, capability grant
Here is the first thing worth seeing. A trivial program prints a string. A slightly less trivial one wants to touch the network — and Jacquard's runtime physically refuses to let it, until I tell it otherwise.
The code for the second file is just this:
net-sample : (Text) ->{Net} Text
net-sample(url) = match fetch(MkRequest(url, "")) {
| MkResponse(_, body) -> body
}
net-sample("https://example.com")
Two things to notice. The signature (Text) ->{Net} Text says the function may perform the Net effect. That row is not a comment — the checker computes it from the call graph and rejects any signature that omits an effect the body performs. And at run time, without --allow net, the program stops with an explicit error before a single packet leaves the machine:
error[E0814]: this program requires the `net` effect, which is not granted
hint: grant it with --allow net, or handle the effect in the program
Exit code 3. Add --allow net and it runs. That is the whole model: what the type says, the runtime enforces. And it enforces it for dynamically loaded code too, which is where the "reviewed for effects" story of most languages quietly falls apart.
For me, this is the single most interesting thing about the language. When I skim generated code, the first question I ask is what can this touch? In Jacquard, the answer is on line one, and I don't have to trust that it's honest — the compiler already checked.
The same policy under three worlds
The install ships a release-risk case study that shows the second big idea: a program written once, then run under different handlers to answer different questions.
The domain is deliberately mundane: given the health of inventory and payments, plus whether there is an error spike, decide Ship, Canary(percent), or Hold(reason). What is unusual is how many kinds of question this one policy answers:
-
Concrete snapshot.
with-snapshot(Healthy, Degraded, False, ...)pins the telemetry effect to specific values and returns one decision:canary(5). -
Probabilistic world.
with-risk-modelreinterprets the sameTelemetryoperations as samples fromDist, conditions on an observed checkout failure, and enumerates the posterior:
0.305077 canary(5)
0.295394 hold("inventory-down")
0.184621 canary(10)
0.155903 hold("payments-down")
0.059005 ship
0.059005 ship is not a threshold to compare against — it's the posterior probability that this policy chooses to ship, given the observed failure. Bayesian evidence, expressed as an ordinary effect operation (observe) handled by an ordinary handler.
-
Exhaustive proof. Warp, the test framework, re-runs the same policy over all 18 telemetry worlds and confirms that when a dependency is
Down,shipis never chosen. Not sampled — enumerated.
Zoom in on the inferred authority block at the top of the screenshot:
release-assessment : () ->{Telemetry} Assessment
with-snapshot : forall a | e. (Health, Health, Bool, () ->{Telemetry | e} a) ->{| e} a
with-risk-model : forall a | e. (() ->{Dist, Telemetry | e} a) ->{Dist | e} a
conditioned-release-risk: () ->{Dist} Decision
Follow the rows. release-assessment requires Telemetry. with-risk-model consumes Telemetry and turns it into Dist. conditioned-release-risk walks out with only Dist in its authority. That flow — which effects a piece of code demands, which effects a caller discharges — is not commentary in the margin. It is the language.
I have written probabilistic models in Python before. The parts that always drift out of sync are: the policy, its mock for testing, and the probabilistic version for what-if analysis. In Jacquard, they are the same 100 lines of code with different handlers on top.
Why this matters when the code was written by a model
I am not an AI-safety researcher. I am someone who spends real hours a week reading code I did not write. Every language I've used treats effects as an emergent property of the call graph — something you infer, at review time, by chasing definitions. That was already an expensive habit. It becomes an unaffordable one when the volume of unfamiliar code goes up.
Jacquard's bet is that the right unit of review is the signature, and that the signature should already tell you the truth. Two consequences follow, and both showed up in the runs above:
-
Prompt-injection-style attacks lose their favorite hiding spot. A hidden
requests.postinside a helper called by a helper cannot exist without appearing in the effect row of the caller, all the way up to the entry point. If the entry point isn't launched with--allow net, the process refuses to run before the payload can trigger. - "Same code, different world" becomes ordinary. Mock libraries, property-based tests, and probabilistic what-if runs are all handler variants of the one policy. Nothing to keep in sync.
The design lineage is explicit: effect rows and handler discipline from Koka, content-addressed identity and cycle hashing from Unison, hygiene from Racket, -fno-lazy gut instinct from Haskell. It reads as a careful synthesis, not a rewrite of the wheel.
What it is not (yet)
I want to be honest about what I ran:
- v0.1 rc3 is a research prototype. The repo ships a
docs/release/0.1/LIMITS.mdand I would take its word for it before shipping this in production. - The ecosystem is one repository. There is no third-party package story.
- Small syntactic gotchas exist (my first Warp test failed because I hadn't caught a
throweffect I introduced by accident). The error was clear; the fix was to compose withthrow.catch. Not a papercut but not zero learning curve either. - The install script needs outbound DNS. Sandboxes that block DNS will need a pre-fetched tarball.
None of these change the shape of the idea. They set the ceiling on how far I'd take it today.
Try it in ten minutes
If you want to check the claims above against your own machine:
# install
curl -fsSL https://raw.githubusercontent.com/jbwinters/jacquard-lang/jacquard-core-0.1-rc3/scripts/install.sh | sh
# smoke test
~/.local/bin/jac run ~/.local/share/jacquard/demos/basics/m1-fact.jac
# → 120
# see the capability gate refuse a net effect
cat > /tmp/net.jac <<'EOF'
net-sample : (Text) ->{Net} Text
net-sample(url) = match fetch(MkRequest(url, "")) {
| MkResponse(_, body) -> body
}
net-sample("https://example.com")
EOF
~/.local/bin/jac run /tmp/net.jac # exit 3, E0814
~/.local/bin/jac run --allow net /tmp/net.jac # exit 0, stubbed response
# see one policy answer three questions
sh ~/.local/share/jacquard/demos/case-studies/release-risk/run.sh
The last command produces the screenshot above on any machine. That is the point of this post — none of what I described is a claim, it is a run.
The shorter version
If you review code that models write, the first question you learn to ask is what can this touch? Jacquard is the first language I've used where that question is answered by the signature and enforced by the runtime, in both directions. It is early, it is small, and it is worth an evening.


Top comments (0)