Clojure: check if a function has certain post (or pre) conditions

520 views Asked by At

Question

In Clojure, it is possible to check if a variable represents a function, using ifn?.

What I'm asking myself is, if there is a way to check if a var is a function which has certain post (or pre) conditions.

Bonus: is it possible to construct the post-condition to be checked parametrically?

Example

Let's say that foo represents a function (i.e. (ifn? foo) returns true). How can I check that foo has a certain post condition, e.g. that it returns numbers between 0 and 10. In other words, how can I check that foo has the following post-condition?

{:post (and (number? %) (<= 0 %) (<= % 10))

Bonus: is it possible to check for N, instead of 10, where N is a parameter I can choose?

{:post (and (number? %) (<= 0 %) (<= % 10))

Background

I'm implementing Bloom-filter data structure, and it would be really nice if I could validate the hash-functions. (I.e., that they are functions returning only numbers between 0 and the number of bits in the Bloom-filter.)

Remarks

I'm already aware of some workarounds, e.g. like wrapping the hash-functions in other functions that contain the post-conditions, or taking their modulo of the number of bits. While I appreciate any further workarounds for the above use case, please, include an explicit answer to the main question (i.e. "is it possible to check if a function has a certain post-condition?") too. "No, it is not possible" is also an answer I can accept of course.

1

There are 1 answers

0
Taylor Wood On BEST ANSWER

[is there] a way to check if a var is a function which has certain post (or pre) conditions

Yes, building on this Q&A, we can do this:

(defn fn->pre-post [fvar]
  (-> fvar meta :arglists first meta (select-keys [:pre :post])))

And write a function taking a function var and a list form to compare to its :post condition(s):

(defn some-post? [fvar post]
  {:pre [(var? fvar)]}
  (some #(= post %) (-> fvar fn->pre-post :post)))

Then we can test to see if a function has a :post condition we expect:

(defn foo [n]
  {:post [(and (number? %) (<= 0 %) (<= % 10))]}
  (* n n))

(some-post?
  #'foo
  '(and (number? %) (<= 0 %) (<= % 10)))
=> true

This is a very brittle equality test. If you reordered the and conditions the equality test would fail even though both ands would be logically identical. You could make the test more robust if you wanted.

is it possible to construct the post-condition to be checked parametrically?

Yes, but again this is going to be brittle/subject to same equality limitations above. In practice, the only difference is we need to specify the form as a kind of template and fill in the blanks before passing it to some-post?. We can define a function that will replace the symbol N in a form with whatever we want:

(defn parameterize-n [n form]
  (walk/postwalk #(if (= 'N %) n %) form))

(parameterize-n
  10
  '(and (number? %) (<= 0 %) (<= % N)))
=> (and (number? %) (<= 0 %) (<= % 10))

We can use this with some-post? like this:

(some-post?
  #'foo
  (parameterize-n
    10
    '(and (number? %) (<= 0 %) (<= % N))))
=> true

As an aside, (<= 0 % 10) is equivalent to (and (<= 0 %) (<= % 10)) but I used your example test verbatim.