## Clojure.Spec Workshop Part 1 - The Foundations

ClojureRemote - February 2017
Yehonathan Sharvit
``````
(str (js/Date.))
``````

## Who am I?

• A mathematician
• A pragmatic theorist
• A coder
• A freak of interactivity
• Founded Audyx - an Audiology Startup with 30K LOCs in Clojurescript
• Author of KLIPSE
• A Clojure consultant

## Agenda

• Clojure.Spec: What's the big deal?
• Basic concepts: conform/validate/explain
• Deeper concepts: sample generation
• Amazing stuff: unit test generation

## Clojure.Spec: What's the big deal?

• Static type vs Dynamic type
• Specifications at the boundaries of the system

## KLIPSE - Code interactivity

Open this presentation in your computer for a collaborative live coding session: http://slides.klipse.tech/clojure-spec-cr17/part1.html#slide-3

(It works also on mobile)

``````
(map inc [1 2 3])
``````

## Clojure.spec - The foundations

Most significant part of Clojure `1.9`.

`clojure.spec` features:

• specification of the structure (the shape) of data
• validation of data
• destructuring data
• data generation based on the specs
• tests generation based on the specs

All you have to do is to require `clojure.spec` like this:

``````
(ns foo.core
(:require [clojure.spec.alpha :as s]))
``````

## Data structure specification

``````
(s/def ::id integer?)
(s/def ::name string?)
``````

Any predicate can be passed to the spec definition:

(A predicate is a function that returns a boolean)

``````
(s/def ::my-big-integer (fn [x] (and (integer? x)
(> x 1000000))))
``````
``````
(s/def ::my-short-string (fn [x] (and (string? x)
(< (count x) 5))))
``````

## Interlude - Namespaced Keywords

Pay attention to the `::`!
Spec definitions must used namespace keywords

``````
(s/def :a-big-integer (fn [x] (and (integer? x)
(> x 1000000))))
``````

Namespaced keywords are like keywords but they are namespaced

The double colon is a syntactic sugar for the current namespace:

``````
::hello
``````

Mandatory in `clojure.spec` because specs are registered in a global registry!

## Data structure validation

Validate data with spec

``````
(s/valid? ::id 19)
``````
``````
(s/valid? ::id "my-id")
``````

And with one of our custom predicates:

``````
(s/valid? ::my-big-integer "abc")
``````
``````
(s/valid? ::my-big-integer 17)
``````

Wouldn't it be great if we could get an explanation about what parts of the spec were not satisfied?

## Data structure validation with explanations

Explain validation

``````
(s/explain-str ::my-big-integer "abc")
``````

Not so useful ðŸ˜•

## Custom predicates - s/and

Big Integer - the real way

``````
(s/def ::big-integer (s/and integer?
#(> % 1000000)))
``````

When it's not an integer:

``````
(s/explain-str ::big-integer "abc")
``````

When it's a small integer:

``````
(s/explain-str ::big-integer 42)
``````

Much better ðŸ˜„

Short String - the real way

``````
(s/def ::short-string (s/and string?
#(< (count %) 5)))
``````

When it's not a string:

``````
(s/explain-str ::short-string 42)
``````

When it's a long string:

``````
(s/explain-str ::short-string "Hello World!")
``````

## Custom predicates - s/or

We must annotate each branch with a tag

``````
(s/def ::big-integer-or-short-string (s/or :int ::big-integer
:str ::short-string))
``````

That makes the explanations about the failure really clear:

``````
(s/explain-str ::big-integer-or-short-string :hello-world!)
``````

## Data parsing with s/conform

Conform is a fancy term for data parsing according to a spec

With primitives, the conformed data is the same as the original data

``````
(s/conform ::id 4200000)
``````

With non-primitives, the conformed data is parsed into a data structure with information about the data

When it's a big integer:

``````
(s/conform ::big-integer-or-short-string 4200000)
``````

When it's a small string:

``````
(s/conform ::big-integer-or-short-string "abc")
``````

When it's neither this nor that:

``````
(s/conform ::big-integer-or-short-string :hello-world!)
``````

It works well with a nested spec

``````
(s/def ::my-special-spec (s/or :keyword keyword?
:bioss ::big-integer-or-short-string))
(s/conform ::my-special-spec "aa")
``````

## Entity maps

You describe the structure of your maps by combining the specs of its keys
and specifying what keys are required and what keys are optional

``````
(s/def ::my-map (s/keys :req [::big-integer]
:opt [::short-string]))
``````

This one is valid:

``````
(s/explain-str ::my-map {::big-integer 90000000
::short-string "Hell"})
``````

This one is invalid for 2 reasons:

``````
(s/explain-str ::my-map {::big-integer 90
::short-string "Hello World!"})
``````

``````
(s/valid? (s/keys) {::big-integer-or-short-string 90})
``````

It's a bit surprising but in spec, ALL the namespace-qualified keys are validated by any registered specs!

Why do we need `opt`?

## Entity maps - unnamespaced keys

You can also have unspaced keywords

``````
(s/def ::my-map-un (s/keys :req-un [::big-integer]
:opt-un [::short-string]))
``````

This one is valid:

``````
(s/explain-str ::my-map-un {:big-integer 90000000
:short-string "Hell"})
``````

This one is invalid for 2 reasons:

``````
(s/explain-str ::my-map-un {:big-integer 90
:short-string "Hello World!"})
``````

``````
(s/valid? (s/keys) {:big-integer-or-short-string 90})
``````

Only the namespace-qualified keys are validated by any registered specs!

## Sequences - Regular Expressions on steroids!

You can desribe the shape of a data sequence using Regular Expressions operators:

An example:

``````
(s/def ::employee (s/cat :name (s/alt :full string?
:first-and-last (s/tuple string? string?))
:salary ::big-integer))
``````
``````
(s/explain-str ::employee '("John Woo" 999))
``````
``````
(s/conform ::employee '(["John" "Woo"] 9999999))
``````

## Sequences - Regular Expressions on steroids! (cont.)

You can describe the shape of a data sequence using Regular Expressions operators:

A simplified version of the args of `defn`:

``````
(s/def ::defn-args (s/cat :name symbol?
:docstring (s/? string?)
:args (s/coll-of symbol? :kind vector?)))
``````

With a doc string : `(defn foo "foo receives two arguments" [a b])`

``````
(s/conform ::defn-args ['foo "foo receives two arguments" '[a b]])
``````

Without a doc string : `(defn foo [a b c d])`

``````
(s/conform ::defn-args ['foo '[a b c d]])
``````

A simplified version of the args of `fn`:

(It's not exactly a collection of symbols)

``````
(s/def ::good-symbol #(and (symbol? %) (not= % '&)))
(s/def ::fn-args (s/and vector?
(s/cat :args (s/* ::good-symbol)
:rest (s/? (s/cat :& '#{&}
:other ::good-symbol)))))
``````
``````
(s/conform ::fn-args '[a b & c])
``````

## `s/alt` vs. `s/or`

Usually inside the spec of a sequence, we use `s/alt`:

``````
(s/def ::seq-alt (s/cat :str string?
:numbers-alt-string (s/alt :nums (s/* number?)
:strs (s/* string?))))
(s/conform ::seq-alt ["hello" 1 2 3])
``````

When we need part of the sequence to be nested, we use `s/or`:

``````
(s/def ::seq-or (s/cat :str string?
:numbers-or-string (s/or :nums (s/* number?)
:strs (s/* string?))))
(s/conform ::seq-or ["hello" [1 2 3]])
``````

## One last thing...

You can describe a spec

``````
(s/describe ::fn-args)
``````

## Interlude

What have we learned?

• The language of specs definition
• How to validate, conform and explain a spec
• The expression power of specs