Clojure functions can attach pre and post conditions, and this seems like it would be useful. Yet I see very little code in the wild which uses this approach? Why has it not caught on?
I agree that it’s useful. It’s probably not used as much because it’s not enforced (like static types) and requires extra effort, like writing specs. And clojure.spec might have partially replaced this functionality, although you could use a combination.
For those wondering what pre and post conditions are in Clojure:
You should also be careful with pre/post conditions as they throw AssertionError’s when triggered. This has two implications:
- Assertions can be disabled in Java with a special JVM flag, making your pre/postconditions silently ignored.
- AssertionError is a sublcass of Error, so it won’t be caught by generic (catch Exception …) handlers people usually use, you have to catch Throwable then. This might lead to errors leaking through catch-all cracks in your program.
In ClojureScript, you can ask the compiler to eliminate all asserts, including pre/post conditions, from the program for production builds using the elide-asserts feature.
There is also https://github.com/ptaoussanis/truss - if you’re considering using pre/post conditions, I think
truss supersedes them due to better error messages and combining pre/post conditions with generic assertions (not just on the input and output of a function).
Even though we now have
clojure.spec, I feel
truss still has a place in documenting certain assumptions we make when dealing with business logic, helps track down weird edge-case bugs in production and works well with
clojure.spec in a single codebase.