Search code examples
clojure

How do you use an existing vector of predicates with :post conditions in Clojure?


Given that :post takes a form that gets evaluated later (e.g. {:post [(= 10 %)]}). How could one dynamically pass a 'pre-made' vector of functions to :post?

For example:

(def my-post-validator
  [prediate1 predicate2 predicate3])
   

(defn foo [x] 
  {:post my-post-validator}
  x)

this throws a syntax error

Don't know how to create ISeq from: clojure.lang.Symbol

With my fuzzy understanding, it's because defn is a macro, and the thing that allows the % syntax in :post is that it's quoted internally..?

I thought maybe I then use a macro to pass a 'literal' of what I wanted evaluated

(defmacro my-post-cond [spec]
  '[(assert spec %) (predicate2 %) (predicate n)])

example:

(defn foo [x]
  {:post (my-post-cond :what/ever)}
  x)

However, this attempt gives the error:

Can't take value of a macro

Is there a way to pass a vector of things to :post rather than having to define it inline?


Solution

  • You can't pass a vector of predefined predicates, but you can combine multiple predicates under a single name and use that name in :post:

    (defn my-post-cond [spec val]
      (and
        ;; Not sure if this is exactly what you want,
        ;; given that `val` becomes an assert message.
        (assert spec val)
        (predicate2 val)
        ;; You used `n` - I assume it was supposed to be `%`.
        (predicate val)))
    
    (defn foo [x]
      {:post [(my-post-cond :what/ever %)]}
      x)