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, invariant clauses from .dfy files
  • Generate clojure.spec.alpha or malli schemas
  • Handle: numeric ranges, string predicates, sequence length, set membership
  • Out of scope: quantifiers (forall, exists), ghost variables, heap predicates

4. Open questions

  1. Should the output be spec or malli? The interop article uses malli for the codec schemas.
  2. How to handle Dafny's seq<char> vs Clojure's String – the type mapping table in the interop article is the starting point.
  3. How to handle modifies clauses – these have no spec equivalent.

5. Related