Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by

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.

2 Answers

0 votes
by
selected by
 
Best answer

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
by

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.

by
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.
...