In this post, we will take the parking garage problem from the previous post and see how `clojure.spec` might be applied. If you are new to clojure.spec you can check out the rationale and the guide on clojure.org. Let’s start by specing the data. Recall that we used a map to state how many parking spaces are available on each garage level:

``````(ns garage-simulation.core)

(def parking-spaces {0 15
1 10})``````

We could spec the components of this map `level` and `number-of-parking-spaces` as positive integers:

``````(require '[cljs.spec :as s])

(s/def ::level (s/and int? (s/or :zero zero? :positive pos?)))
(s/def ::number-of-parking-spaces (s/and int? pos?))``````

`parking-spaces` can then be specd like this:

``(s/def ::parking-spaces (s/map-of ::level ::number-of-parking-spaces))``

We can then check the validity using `clojure.spec/valid?`:

``(s/valid? ::parking-spaces {0 15 1 10})``

If we were to try an invalid map:

``(s/valid? ::parking-spaces {0 15 1 "10"})``

If we’re confused as to why our data is not valid, we can use the `clojure.spec/explain` function to figure out what’s wrong:

``(s/explain ::parking-spaces {0 15 1 "10"})``

or `clojure.spec/explain-data` to get a map we can more easily traverse:

``(s/explain-data ::parking-spaces {0 15 1 "10"})``

Using this spec we can even generate some valid samples using the `clojure.spec.gen/generate` or `clojure.spec.gen/sample` functions:

``````(require '[cljs.spec.impl.gen :as g]
'[clojure.test.check :as t])

(g/generate (s/gen ::parking-spaces))``````
`=> {2868014 31, 3118 28, 46838612 227837, 2631 940026, 109 959746, 275023 58, 61032 3846, 24482 11839, 4370470 92, 1826 412743, 47082 2125773, 190 236375948}`
``(g/sample (s/gen ::parking-spaces))``
`=> ({0 1, 2 3} {0 1, 1 1} {0 1, 1 189, 3 2} {1 1, 0 15, 3 1, 2 6} {0 1, 1 5, 4 5, 15 3, 22 6, 6 1, 3 1, 2 2, 8 5} {} {17 10, 5 10, 2 3} {0 7, 30 25, 48 22, 7 117, 10 19, 31 325} {0 2, 1 46, 39 677, 13 69, 6 9, 3 6, 2 712, 23 1, 19 31, 115 21, 5 5} {0 84, 1 2, 15 1, 355 37})`

At this point you can see that these samples don’t really match reality because in parking garages levels usually start at 0 and increase consecutively. We could spec this on the map of course, but even better would be to switch to a data structure which already has these constraints i.e. just use a vector :)

``````(def parking-spaces [15 10])

(s/def ::parking-spaces (s/coll-of ::number-of-parking-spaces :kind vector?))

(g/generate (s/gen ::parking-spaces))``````

⇒ [359765 242401325 199244 8685 227771037 10073702 21779006 24302758 347572 6381197 84 1488130 1008076 293347674 135 3109580]

might be tough to make it out of this garage :)

Having done this change we will need to tweak `intialize-garage!` a bit:

``````(defn initialize-garage!
"Sets the initial state of the garage."
[parking-spaces]
(dosync
(ref-set vehicles {})
(ref-set empty-parking-spaces
(into #{}
(for [[level spaces] (map-indexed vector parking-spaces)
space-number   (range spaces)]
[level space-number])))))``````

Moving on to `licence plates`, these are simply strings so we can spec them like this:

``(s/def ::licence-plate string?)``

There is no global standard for licence plates but if there were we could use a regex to spec the licence plates further. For example if a licence plate were defined as 4 upper case characters followed by 3 digits:

``````(def licence-plate-regex #"[A-Z]{4}\d{3}")
(s/def ::licence-plate (s/and string? #(re-matches licence-plate-regex %)))``````

We represented the location for a `parking-space` using a vector of two integers: `[level space]`. We can define this as well:

``(s/def ::parking-space (s/coll-of (s/and int? (s/or :zero zero? :positive pos?)) :kind vector? :count 2))``

Finally we can spec the map used to store the locations of the vehicles in the garage:

``````(s/def ::vehicles (s/map-of ::licence-plate ::parking-space))

(s/valid? ::vehicles {"ASDF001" [0 3]}) ;; => true``````

Now that we’ve specd the data let’s move on to the functions. Starting with `initialize-garage!` we use `clojure.spec/fdef` and specify one argument, namely `::parking-spaces`:

``````(s/fdef initialize-garage!
:args (s/cat :parking-spaces ::parking-spaces))``````

For `locate-vehicle` we can also specify the return value:

``````(s/fdef locate-vehicle
:args (s/cat :licence-plate ::licence-plate)
:ret (s/or ::parking-space nil?))``````

`number-of-free-parking-spaces` takes no args but we can specify the return:

``````(s/fdef number-of-free-parking-spaces
:ret (s/and int? (s/or :zero zero? :positive pos?)))``````

For `enter-garage!` we can also specify a relationship between the argument and the return value, namely that the return value should contain the licence plate:

``````(s/fdef enter-garage!
:args (s/cat :licence-plate ::licence-plate)
:ret (s/or ::vehicles nil?)
:fn #(contains? (:ret %) (-> % :args :licence-plate)))``````

And similarly for `exit-garage!` we can specify that the licence plate for the vehicle that just exited the garage should not be present in the return value:

``````(s/fdef exit-garage!
:args (s/cat :licence-plate ::licence-plate)
:ret (s/or ::vehicles nil?)
:fn #(not (contains? (:ret %) (-> % :args :licence-plate))))``````

If we turn on instrumentation on a function and then call it with invalid parameters we get an exception with the details of went wrong. In the following examples the first says "Sorry you gave me a number but I need a string", the second says "Sorry your licence plate doesn’t match the regex":

``````(t/instrument `enter-garage!)

(enter-garage! 3)
; ExceptionInfo Call to #'garage-simulation.core/enter-garage! did not conform to spec:
; In: [0] val: 3 fails spec: :garage-simulation.core/licence-plate at: [:args :licence-plate] predicate: string?
; :clojure.spec/args  (3)
; :clojure.spec/failure  :instrument
; :clojure.spec.test/caller  {:file "form-init6892093246791959693.clj", :line 82, :var-scope garage-simulation.core/eval20779}
;   clojure.core/ex-info (core.clj:4724)

(enter-garage! "ASDF01")
; ExceptionInfo Call to #'garage-simulation.core/enter-garage! did not conform to spec:
; In: [0] val: "ASDF01" fails spec: :garage-simulation.core/licence-plate at: [:args :licence-plate] predicate: (re-matches licence-plate-regex %)
; :clojure.spec/args  ("ASDF01")
; :clojure.spec/failure  :instrument
; :clojure.spec.test/caller  {:file "form-init6892093246791959693.clj", :line 75, :var-scope garage-simulation.core/eval20777}
;   clojure.core/ex-info (core.clj:4724)``````

Another thing we can do with our specs is something called `property based testing`. In `unit testing` we usually write tests for specific test cases like we did in the previous post with midje. In property based testing we use a framework (in this case test.check) to automatically generate a range of test cases against which the invariants defined in our spec are verified. We do this using the `clojure.spec.test/check` function:

``````(clojure.spec.test/check `enter-garage!)
; ExceptionInfo Couldn't satisfy such-that predicate after 100 tries.  clojure.core/ex-info (core.clj:4724)``````

Whoops! The problem here is that `test.check` tried to generate random strings for licence plates for `enter-garage!` but gave up after a 100 tries because they all did not conform to the regex we defined earlier. This would also happen if we directly tried to generate samples for licence plates:

``````(g/sample (s/gen ::licence-plate))
; ExceptionInfo Couldn't satisfy such-that predicate after 100 tries.  clojure.core/ex-info (core.clj:4724)``````

We can fix this by associating a generator with the spec for licence plates. We can use the test.chuck library for this which provides a handy `string-from-regex` generator:

``````(s/def ::licence-plate
(s/with-gen
(s/and string? #(re-matches licence-plate-regex %))
#(cg/string-from-regex licence-plate-regex)))``````

So now we can generate licence plates at will:

``````(g/sample (s/gen ::licence-plate))
;; => ("YZJY672" "WDPR193" "BMAX543" "BIEL908" "VNJC192" "ZKFA361" "HLYS035" "DAIA703" "WFGS654" "LPSX140")``````

Let’s move on to verifying the invariant for `enter-garage!`:

``````(t/check `enter-garage!)
; ({:spec #object[clojure.spec$fspec_impl$reify__13789 0x1fdaac28 "[email protected]"], :clojure.spec.test.check/ret {:result #error {
;  :cause "Specification-based check failed"
;  :data {:clojure.spec/problems [{:path [:fn], :pred (contains? (:ret %) (-> % :args :licence-plate)), :val {:args {:licence-plate "SCWE626"}, :ret nil}, :via [], :in []}], :clojure.spec.test/args ("SCWE626"), :clojure.spec.test/val {:args {:licence-plate "SCWE626"}, :ret nil}, :clojure.spec/failure :check-failed}
;  :via
;  [{:type clojure.lang.ExceptionInfo
;    :message "Specification-based check failed"
;    :data {:clojure.spec/problems [{:path [:fn], :pred (contains? (:ret %) (-> % :args :licence-plate)), :val {:args {:licence-plate "SCWE626"}, :ret nil}, :via [], :in []}], :clojure.spec.test/args ("SCWE626"), :clojure.spec.test/val {:args {:licence-plate "SCWE626"}, :ret nil}, :clojure.spec/failure :check-failed}
;   ...``````

This doesn’t look good at all :) The reason it happens is that when `check` is called it generates a large number of inputs which in our case exceeds the available space in the garage. This actually points out a problem in the invariant i.e. the vehicle doesn’t make it into the garage if there is no space available. We can redefine the invariant to accommodate this:

``````(s/fdef enter-garage!
:args (s/cat :licence-plate ::licence-plate)
:ret ::vehicles
:fn #(or (nil? (:ret %))
contains? (:ret %) (-> % :args :licence-plate)))

(t/check `enter-garage!)
; ({:spec #object[clojure.spec$fspec_impl$reify__13789 0x4d8e87aa "[email protected]"], :clojure.spec.test.check/ret {:result true, :num-tests 1000, :seed 1469535504589}, :sym garage-simulation.core/enter-garage!})``````

Much better. Happy specing!