Dafny spec to clojure.spec Transpiler
Table of Contents
1. Motivation
The Dafny-Clojure interop article covers the JVM boundary:
Dafny compiles to Java, Clojure calls it. But Dafny's requires and
ensures clauses are ghost-erased at compile time – the JAR carries
no trace of the preconditions. If Clojure passes invalid data across
the boundary, the "verified" code may crash on a condition Dafny
proved impossible under its assumptions.
This note explores the inverse direction: parse .dfy source and
generate clojure.spec.alpha definitions that enforce Dafny's
contracts at runtime.
2. The trust gap
Dafny source --> JAR (ghost-erased) --> Clojure caller requires P(x) (no P check) passes x without P? ensures Q(r) (no Q check) trusts r blindly?
The spec transpiler closes this: requires P(x) becomes (s/def ::x P-spec).
3. Scope
- Parse
requires,ensures,invariantclauses from.dfyfiles - Generate
clojure.spec.alphaormallischemas - Handle: numeric ranges, string predicates, sequence length, set membership
- Out of scope: quantifiers (
forall,exists), ghost variables, heap predicates
4. Open questions
- Should the output be spec or malli? The interop article uses malli for the codec schemas.
- How to handle Dafny's
seq<char>vs Clojure'sString– the type mapping table in the interop article is the starting point. - How to handle
modifiesclauses – these have no spec equivalent.
5. Related
- Dafny-Clojure JVM Interop (parent article, SG-2 gap)
- Lean 4 codec proofs (the type-level version of this)
- Type systems, one tool four ways (comparative perspective)