# function spec challenge

What is the simplest way to spec this function? Specifically, the `:fn` portion

``````(s/def ::edges #{0.149 0.150 0.050})
(s/def ::result #{:correct :incorrect})

(s/fdef foo
:args (s/cat :student-response ::edges :authoritative-answer ::edges)
:ret ::result
:fn ? )
``````

Basically, if the `student-response` rounded to the tenths place matches the `authoritative-answer` rounded to the tenths place, the function returns `:correct`, otherwise `:incorrect`.

I selected edges that would hit the rounding boundaries rather than hoping the generative process for numbers would stumble on those edges.

selected

Okay. Below is what I used and it works. Is there a simpler way?

``````(defn- round [some-number to]
(double (/ (Math/round (double (* (/ 1 to) some-number))) (/ 1 to))))

(s/def ::edges #{0.149 0.150 0.050})
(s/def ::result #{:correct :incorrect})

(s/fdef foo
:args (s/cat :student-value ::edges :authoritative-value ::edges)
:ret ::result
:fn (s/or
:correct-answer (s/and #(= (round (-> % :args :student-value) 0.1)
(round (-> % :args :authoritative-value) 0.1))
#(= (:ret %) :correct))
:incorrect-answer (s/and #(not= (round (-> % :args :student-value) 0.1)
(round (-> % :args :authoritative-value) 0.1))
#(= (:ret %) :incorrect))
)
)
``````
+1 vote

I suspect you'd just be replicating the logic that's in foo for making this decision and thus I don't think adding a :fn would be particularly valuable. Usually it's only useful to add a :fn if it can verify some constraint between the args and ret that is independent from the logic of the function itself.

For me, tests do represent a kind of redundancy, similar to double-entry bookkeeping. So I am writing this spec to execute in a clojure.test to validate the implementation of the function, as an alternative to writing a regular test.

The example I provided is aimed at helping me understand how to best express these types of relationships in specs, as I am new to this.