If I've understood clojure.spec/fdef
correct it allows us to make specifications like the described in the question.
(spec/fdef ::predicate-1
:args (spec/cat :arg any?)
:ret boolean?)
Which we can test by passing it some examples which we know should pass or fail the test:
(spec/valid? ::predicate-1 boolean?) => true
(spec/valid? ::predicate-1 (fn [a] 5)) => false
(spec/valid? ::predicate-1 (fn [a] true)) => true
(spec/valid? ::predicate-1 (fn [a b] true))=> false
(spec/valid? ::predicate-1 #(= 10 %)) => true
(spec/valid? ::predicate-1 (fn [a] nil)) => false
For defenition nr. 2:
(spec/fdef ::predicate-2
:args (spec/cat :arg any?)
:ret (spec/nilable boolean?))
(spec/valid? ::predicate-2 (fn [a] nil)) => true
And for nr. 3 any function which takes one argument is valid since everything in clojure is either truthy or falsey.
(spec/fdef ::predicate-3
:args (spec/cat :arg any?)
:ret any?)
(spec/valid? ::predicate-3 identity) => true
(spec/valid? ::predicate-3 str) => true
One interesting thing we then seem to be able to do is have spec generate such functions for us:
(let [p (gen/generate (spec/gen ::pedicate-1))]
(clojure.string/join
" " [(p 0) (p 1) (p -1) (p nil) (p 'a) (p :a) (p (fn [a] a))]))
=> "false true true false true false false"
And out of that we can perhaps try to guess what the generated function does. But without being able to see the source we'll have a hard time checking whether our guess was correct or not.