My new position requires me to get familiar with the Clojure language. In intend to document what I learn in a series of posts, to serve as my personal reference notes. As a side-effect, I hope it will also be beneficial to others who want to take the same path. There are already a multitude of great tutorials available: hence, each post will focus on a specific theme, that is specific to Clojure considering that most of my experience comes from OOP .

This is the 2nd post in the Learning Clojure focus series.Other posts include:

As a newcomer to Clojure, a big issue of mine is the lack of types. This is not specific to Clojure. I miss types in JavaScript, Groovy, Python, etc. While I value dynamic languages ease of use when writing scripts, my bread-and-butter is still to develop regular applications. With that in mind, I prefer to let the compiler catch type-related errors: that means focusing on actual business features, instead of writing tests to catch those errors.

While Clojure doesn’t offer types in the language syntax, its design allows to build similar capabilities via constructs. Even better, there’s an existing library to take care of that, aptly named spec.

spec is available out-of-the-box since Clojure 1.9. Earlier versions require to explicitly add the dependency on the classpath.

Basics To start using spec, just require the clojure.spec.alpha package in the namespace: ( ns ch.frankel.blog.clojure.spec ( :require [ clojure.spec.alpha :as sparc ])) The next step is to define the expected type of a value, using the def function. It accepts two parameters: # Name Description 1 k Symbol name 2 spec-form Predicate More valid parameter values are possible, but this is quite enough to start with. For simple values, this is quite straightforward: ( spec/def ::nil nil? ) (1) ( spec/def ::bool boolean? ) (2) ( spec/def ::string string? ) (3) 1 Defines ::nil as the nil value 2 Defines ::bool as a boolean value 3 Defines ::string as any string value Keywords are symbolic identifiers that evaluate to themselves. They provide very fast equality tests. Like Symbols, they have names and optional namespaces, both of which are strings. The leading ':' is not part of the namespace or name. — Clojure documentation

https://clojure.org/reference/data_structures#Keywords The :: syntax is the shortcut for a qualified keyword, one fully-qualified using the current namespace. For example, ::bool above resolves to :ch.frankel.blog.clojure.spec/bool . This technique is not limited to simple types. It’s also possible to restrict values to an enumeration: ( spec/def ::direction # { ::NORTH ::EAST ::SOUTH ::WEST })

Spec checks Once a spec has been def’ed, there are two different ways to use it. The valid? function returns a boolean , depending whether a value conforms (or not) to the spec e.g.: Source Conforms? Returns ( spec/valid? ::nil nil ) true ( spec/valid? ::string "f" ) true ( spec/valid? ::nil "f" ) false ( spec/valid? ::string nil ) false The conform? function returns: the value if conforms to the spec

or clojure.spec.alpha/invalid if it does’t Source Conforms? Returns ( spec/conform? ::nil nil ) nil ( spec/conform? ::string "f" ) "f" ( spec/conform? ::nil "f" ) clojure.spec.alpha/invalid ( spec/conform? ::string nil ) clojure.spec.alpha/invalid

Custom spec functions The above code uses out-of-the-box functions e.g. nil? and string? . There are a lot of similar functions. Here’s a sample, taken from clojure.core : Function Checks wether the parameter is…​ keyword? a keyword (obviously…​) symbol? a symbol (obviously as well) ident? a symbol or a keyword uuid? a java.util.UUID instance uri? a java.net.URI instance While some use-cases are covered, a lot of specific ones are not. In that case, any function that accepts an argument and returns a boolean can be used. Here’s a function that checks whether the parameter is a LocalDate , and how it can be used: ( defn local-date? "Check if the parameter is a java.time.LocalDate instance" [ x ] ( instance? LocalDate x )) ( spec/def ::date local-date? ) ( spec/valid? ::date ( LocalDate/of 2009 1 1 )) (1) ( spec/valid? ::date "f" ) (2) 1 Evaluate to true 2 Evaluate to false

Spec’ing data structures Now that we know how to spec simple values, it’s time to spec more complex ones. In Clojure, one common way to model an "entity" is to use a data map. My favorite example is a Person entity, with the following properties: First name

Last name

Birthdate It can be spec’ed using the keys function. Parameters allow to specify which keys are required, and which ones are optional: ( spec/def ::first-name string? ) ( spec/def ::last-name string? ) ( spec/def ::birthdate local-date? ) ( spec/def ::person ( spec/keys :req [ ::first-name ::last-name ] (1) :opt [ ::birthdate ])) (2) 1 Required values 2 Optional value Here are some samples, and some associated validity checks: Source Returns Rationale ( spec/valid? ::person { ::first-name "John" ::last-name "Doe" ::birthdate ( LocalDate/of 1970 1 1 )}) true ( spec/valid? ::person "f" ) false string is not a map ( spec/valid? ::person { ::last-name "Doe" ::birthdate ( LocalDate/of 1970 1 1 )}) false map doesn’t contain a value under the ::first-name key ( spec/valid? ::person { ::first-name "John" ::last-name "Doe" }) true birthdate is not required ( spec/valid? ::person { ::first-name "John" ::last-name "Doe" ::birthdate "Unknown" }) false birthdate is not a LocalDate ( spec/valid? ::person { ::first-name "John" ::last-name "Doe" ::birthdate ( LocalDate/of 1970 1 1 ) ::title "Mr" }) true Additional entries are fine

Spec’ing collections The next step is to use spec to validate the type of elements in a collection, just like with generics in Java e.g List<T> , Map<T> or Set<T> . This is achieved with the help of additional functions: coll-of for "standard" Clojure collection, vector or list , etc.

map-of for maps For example, to spec a list of LocalDate is quite straightforward: ( spec/def ::dates ( spec/coll-of ::date )) Likewise, for a map of keyword / LocalDate : ( spec/def ::map-dates ( spec/map-of keyword? ::date )) Of course, it works also with data structures: ( spec/def ::map-persons ( spec/map-of keyword? ::person ))

Conclusion While Clojure is a dynamically-typed language, it’s possible to supplement types by using the spec library. It allows to validate simple types, enumerations, maps and collections, just as with any statically-typed language.