Skip to content

Instantly share code, notes, and snippets.

@KingCode
Created October 20, 2016 03:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KingCode/101ff425f1abd9693a7e8c4b5d389e74 to your computer and use it in GitHub Desktop.
Save KingCode/101ff425f1abd9693a7e8c4b5d389e74 to your computer and use it in GitHub Desktop.
clojure.spec usage where instrumenting a function seems to cause exercise-fn to generate non-compliant input?? See line 61 below
(require '[clojure.spec :as s]
'[clojure.spec.test :as stest])
(defn fizzbuzz
([m1 m2 n]
(let [ c #(->> % vector (concat (-> %2 dec (repeat nil))) cycle)
fb-re #"Fizz|Buzz|FizzBuzz"]
(->> [(c "Fizz" m1), (c "Buzz" m2)]
(apply map
(fn [i m1 m2]
(or (re-matches fb-re (str m1 m2))
i))
(range 1 (inc n))))))
([n]
(fizzbuzz 3 5 n)))
(s/def ::posint (s/and integer? pos?))
(s/def ::fizzbuzz (s/and
(s/alt :_1-arg (s/cat :limit ::posint)
:_3-arg (s/cat :n1 (s/and ::posint #(< 1 %))
:n2 ::posint
:limit ::posint))
(fn [[_ {:keys [n1 n2 limit]}]]
(if n1
(< n1 n2 limit)
(< 0 limit)))))
(def fizzbuzz-inout-valid?
#(let [mult1 (or (-> % :args :_3-arg :n1) 3)
mult2 (or (-> % :args :_3-arg :n2) 5)
n (or (-> % :args :_3-arg :limit)
(-> % :args :_1-arg))]
(and (= n (count (-> % :ret)))
(->> (-> % :ret)
(map (fn [i x]
(cond
(= 0 (rem i mult1)(rem i mult2))
(= "FizzBuzz" x)
(zero? (rem i mult1))
(= "Fizz" x)
(zero? (rem i mult2))
(= "Buzz" x)
:else
(= i x)))
(range 1 (inc n)))
(every? identity)))))
(s/fdef fizzbuzz
:args ::fizzbuzz
:ret (s/* (s/alt :num ::posint
:fizz #(= "Fizz" %)
:buzz #(= "Buzz" %)
:fizzbuzz #(= "FizzBuzz")))
:fn fizzbuzz-inout-valid?)
;; This needs to be commented out in order to
;; prevent a spec failure on the next one!?..
(stest/instrument `fizzbuzz)
(s/exercise-fn `fizzbuzz)
;; ..
;; => ExceptionInfo Call to #'user/fizzbuzz did not conform to spec:
;; val: [:_3-arg {:n1 3, :n2 5, :limit 1}] fails at: [:args] predicate: (fn [[_ {:keys [n1 n2 limit]}]] (if n1 (< n1 n2 limit) (< 0 limit)))
;; :clojure.spec/args (3 5 1)
;; :clojure.spec/failure :instrument
;; :clojure.spec.test/caller {:file "form-init96424067756483555.clj", :line 58, :var-scope user/fizzbuzz}
;; clojure.core/ex-info (core.clj:4725)
@KingCode
Copy link
Author

KingCode commented Oct 20, 2016

The reason is a conflict b/w the 1- and 3-arity args specs, as explained here. I overconstrained limit, which could actually be any non-negative number (size of output), .e.g
replacing if clause @line 24 with (if n1 (< n1 n2) true)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment