Zero penalty runtime assertions for Clojure.
MPL-2.0 License
#+STARTUP: hidestars showall
assertions Zero penalty runtime assertions for Clojure.
Clojure's built in assert macro is a compile-time construct that will
eliminate assertions if assert is set to false. However, there isn't a
good way to set assert to false. You cannot set! it in your namespace,
because that will fail when your code is loaded in any context where there
isn't an established binding for assert (AOT'ed code for one). Otherwise
you could try to alter-var-root assert in a user.clj file, but that
does not compose well. You can set a Java system property to configure some
features of the Clojure compiler, but there doesn't exist a property for
disabling assertions.
Even if you could work that all out, what you have is still a compile-time assertion construct. Java's assertions have the property that they can be enabled/disabled at runtime without any performance penalty.
My assertions work similar to Java's. There is a pjstadig.Assertions class
with a static final field that is initialized by calling
desiredAssertionStatus for that class. When this static final field set to
false and it is used in a conditional, then the body of that conditional
(i.e. the assertion) is eliminated as dead code by the JIT.
To use this for a Leiningen project
: [pjstadig/assertions "0.2.0"]
Or for a Maven project
: : pjstadig : assertions : 0.2.0 :
Example:
: (ns pjstadig.assertions.test : (:refer-clojure :exclude [assert]) : (:require [pjstadig.assertions :refer [assert]])) : : (defn -main [& [flag]] : (assert (not= flag "fail")))
When you run the program with assertions enabled it throws an AssertionError:
: ~/src/assertions$ JVM_OPTS=-ea lein run -m pjstadig.assertions.test fail : Exception in thread "main" java.lang.AssertionError: (not= flag "fail") : at pjstadig.assertions.test$_main.doInvoke(test.clj:14) : at clojure.lang.RestFn.invoke(RestFn.java:408) : at clojure.lang.Var.invoke(Var.java:415) : at user$eval33.invoke(NO_SOURCE_FILE:1) : at clojure.lang.Compiler.eval(Compiler.java:6619) : at clojure.lang.Compiler.eval(Compiler.java:6609) : at clojure.lang.Compiler.eval(Compiler.java:6582) : at clojure.core$eval.invoke(core.clj:2852) : at clojure.main$eval_opt.invoke(main.clj:308) : at clojure.main$initialize.invoke(main.clj:327) : at clojure.main$null_opt.invoke(main.clj:362) : at clojure.main$main.doInvoke(main.clj:440) : at clojure.lang.RestFn.invoke(RestFn.java:421) : at clojure.lang.Var.invoke(Var.java:419) : at clojure.lang.AFn.applyToHelper(AFn.java:163) : at clojure.lang.Var.applyTo(Var.java:532) : at clojure.main.main(main.java:37)
When you run it with assertions disabled no AssertionError is thrown, and there is no penalty for including assertions in your code:
: ~/src/assertions$ lein run -m pjstadig.assertions.test fail : ~/src/assertions$
You can also enable/disable assertions specifically for the pjstadig package
(e.g. -ea:pjstadig...), or the pjstadig.Assertions
(e.g. -ea:pjstadig.Assertions) class. However, doing so will enable/disable
assertions globally for any namespace that is using these assertions. There
is currently no way to enable/disable assertions for individual Clojure
namespaces.
** License
: Copyright © Paul Stadig and contributors. All rights reserved.
:
: This Source Code Form is subject to the terms of the Mozilla Public License,
: v. 2.0. If a copy of the MPL was not distributed with this file, You can
: obtain one at http://mozilla.org/MPL/2.0/.
:
: This Source Code Form is "Incompatible With Secondary Licenses", as defined
: by the Mozilla Public License, v. 2.0.