Thanks for taking the effort to contribute to this project and thanks for taking the effort to read through this document first. It's appreciated.
- Don't include more than one spec or fix in a single PR.
- If you want to change existing specs, first read this document, previous issues and PRs related to those specs to inform yourself of prior discussions and decisions.
- Don't include changes unrelated to the purpose of the PR. This includes changing the project version number, adding lines to the .gitignore file, or changing the indentation or formatting.
- Don't open a new PR if changes are requested. Just push to the same branch and the PR will be updated.
- If you have any questions, you're welcome to discuss these in #speculative on Clojurians Slack.
- Mention the Github issue number in the title of the commit.
Be as general as possible, while still being correct.
-
Prefer named specs over predicates e.g.
::ifn
instead ofifn?
(#76) -
Use
associative?
instead ofmap?
orvector?
(#46) -
Use
ifn?
instead offn?
(#42) -
Use
s/alt
inside for arity alternatives in favor ofs/or
::args (s/alt :infinite (s/cat ...) :finite (s/cat ...))
- Sequence conceptually accept and return
seqable?
, notseq?
. Sequence functions compose because they callseq
on theirseqable?
argument, not because they receive or return aseq?
. Don't spec implementation details such as the difference between()
andnil
in return values. See (#45).
- Use consistent naming throughout the codebase.
- Try to use descriptive names for arguments, arities, etc. and use inspiriration from the docstring:
(s/fdef clojure.core/range
:args (s/alt :infinite (s/cat)
:finite (s/cat :start (s/? number?)
:end number?
:step (s/? number?)))
:ret seqable?)
vs.
(s/fdef clojure.core/range
:args (s/alt :arity-0 (s/cat)
:arity-1-3 (s/cat :n1 (s/? number?)
:n2 number?
:n3 (s/? number?)))
:ret seqable?)
- Follow the Clojure style guide.
- Sort specs and tests by the way they appear in clojure/core.clj when possible.
Provide extensive tests for each spec.
A test for an fdef
consists of thee parts:
- Example based tests for arguments that should be accepted (must have)
- Generative tests using
respeced.test/check
(nice to have) - Example based tests for arguments that should be rejected (must have)
Example:
(deftest interpose-test
;; 1) example based tests for arguments that should be accepted
(is (check-call `interpose [0]))
(is (check-call `interpose [0 [1 1 1]]))
;; 2) generative tests
(check `interpose)
;; 3) example based tests for arguments that should be rejected
(with-instrumentation `interpose
(testing "wrong amount of args"
(is (caught? `interpose (interpose))))
(testing "non-coll arg"
(is (caught? `interpose (interpose 0 0))))))
After writing a new spec, for it to be instrumentable using
speculative.instrument/instrument
, run:
script/update-syms
This will update the file src/speculative/impl/syms.cljc
.
Some functions will not instrumentable on all environments or have no point in
being instrumented (e.g. some?
, str
). In that case you can update the
blacklist(s) in blacklist.edn
.
Some of these guidelines have been inspired by the guidelines in medley.