defverified: Inline Dafny Macros for Clojure
Table of Contents
1. Motivation
The claimed Java name-mangling convention (=_ModuleName._static_Methods.myMethod=) does not match reality. The sibling [[file:../dafny-clj-interop/index.org][Dafny-Clojure interop]] note's execution-validated REPL transcript (its own =:EXEC_VERDICT: verified=) shows the real shape: a top-level/default-module method compiles to =_System.__default.MethodName= (called as =_System.__default/Rot13=), and a named module =Foo= compiles to =Foo_Compile.__default.MethodName= (called as =Involutions_Compile.__default/Rot13=). There is no =_static_Methods= class -- the Dafny reference manual confirms module-scope functions and methods compile as static members of the implicit =__default= class. Corrected below.
The Dafny-Clojure interop stays at the namespace boundary:
compile externally, call via Java interop. The developer experience
requires knowing Dafny's name-mangling conventions
(Foo_Compile.__default.myMethod for a named module, or
_System.__default.myMethod for the top-level module) and managing a
separate build step.
defverified is a macro that moves the boundary inside the .clj file:
(defverified sum-list [xs]
{:requires [(all-ints? xs)]
:ensures [(== % (apply + xs))]}
"method Sum(s: seq<int>) returns (r: int)
requires forall i :: 0 <= i < |s| ==> s[i] >= 0
ensures r == Sum(s)
{ r := 0; for i := 0 to |s| { r := r + s[i]; } }")
2. Challenges
- Latency: Dafny verification takes seconds to minutes. Acceptable at compile time, not at REPL eval time.
- Error propagation: Dafny's verification failures must map to Clojure compile errors with useful source locations.
- Naming: The macro must generate deterministic Java class names from the Clojure var name.
- Caching: Recompile only when the Dafny source string changes (content-addressed).
3. Design sketch
defverified macro 1. Hash the Dafny source string 2. Check cache (~/.dafny-cache/<hash>.jar) 3. If miss: write temp .dfy, invoke dafny build --target:java, compile to JAR 4. Add JAR to classpath (dynamic classloader) 5. Generate wrapper fn with spec checks from :requires/:ensures 6. Return the wrapped fn
4. Related
- Dafny-Clojure JVM Interop (the boundary this macro hides)
- Dafny spec transpiler (generates the spec checks this macro uses)