From 0ac482878a1dd520cbee2faa0f5f6ab1082ffa76 Mon Sep 17 00:00:00 2001 From: Rich Hickey Date: Wed, 24 Jun 2009 15:21:42 -0400 Subject: [PATCH] :pre and :post conditions as metadata on arglist, or map following arglist conditions are predicate exprs in a vector return value of fn is bound to % for :post (defn foo [x y] {:pre [(even? x) (< x y)] :post [(> % 3)]} (* x y)) add *assert*, default true, when not true asserts are no-ops *assert* is bound in repl --- src/clj/clojure/core.clj | 28 +++++++++++++++++++++++----- src/clj/clojure/main.clj | 1 + src/jvm/clojure/lang/RT.java | 1 + 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index e8068dff33..ba67476383 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -2607,7 +2607,7 @@ (even? (count bindings)) "an even number of forms in binding vector") `(let* ~(destructure bindings) ~@body)) -;redefine fn with destructuring +;redefine fn with destructuring and pre/post conditions (defmacro fn "(fn name? [params* ] exprs*) (fn name? ([params* ] exprs*)+) @@ -2623,9 +2623,26 @@ sigs (if name (next sigs) sigs) sigs (if (vector? (first sigs)) (list sigs) sigs) psig (fn [sig] - (let [[params & body] sig] + (let [[params & body] sig + conds (when (and (next body) (map? (first body))) + (first body)) + body (if conds (next body) body) + conds (or conds ^params) + pre (:pre conds) + post (:post conds) + body (if post + `((let [~'% ~(if (< 1 (count body)) + `(do ~@body) + (first body))] + ~@(map (fn [c] `(assert ~c)) post) + ~'%)) + body) + body (if pre + (concat (map (fn [c] `(assert ~c)) pre) + body) + body)] (if (every? symbol? params) - sig + (cons params body) (loop [params params new-params [] lets []] @@ -2794,8 +2811,9 @@ "Evaluates expr and throws an exception if it does not evaluate to logical true." [x] - `(when-not ~x - (throw (new Exception (str "Assert failed: " (pr-str '~x)))))) + (when *assert* + `(when-not ~x + (throw (new Exception (str "Assert failed: " (pr-str '~x))))))) (defn test "test [v] finds fn at key :test in var metadata and calls it, diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj index ee185afe7c..6bd798daf1 100644 --- a/src/clj/clojure/main.clj +++ b/src/clj/clojure/main.clj @@ -27,6 +27,7 @@ *print-level* *print-level* *compile-path* (System/getProperty "clojure.compile.path" "classes") *command-line-args* *command-line-args* + *assert* *assert* *1 nil *2 nil *3 nil diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index 728a2a765f..d0cb01f681 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -179,6 +179,7 @@ public class RT{ final static Keyword TAG_KEY = Keyword.intern(null, "tag"); final static public Var AGENT = Var.intern(CLOJURE_NS, Symbol.create("*agent*"), null); final static public Var READEVAL = Var.intern(CLOJURE_NS, Symbol.create("*read-eval*"), T); +final static public Var ASSERT = Var.intern(CLOJURE_NS, Symbol.create("*assert*"), T); final static public Var MACRO_META = Var.intern(CLOJURE_NS, Symbol.create("*macro-meta*"), null); final static public Var MATH_CONTEXT = Var.intern(CLOJURE_NS, Symbol.create("*math-context*"), null); static Keyword LINE_KEY = Keyword.intern(null, "line");