assertions icon indicating copy to clipboard operation
assertions copied to clipboard

Zero penalty runtime assertions for Clojure.

#+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

    : : <groupId>pjstadig</groupId> : <artifactId>assertions</artifactId> : 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.