Skip to content
Brian Marick edited this page Oct 12, 2015 · 8 revisions

The structural-typing.type namespace contains both functions that create types and also those that check them. That's convenient for examples and playing around at the repl. In your code, I recommend you isolate type creation in one namespace, create in it checking functions that hide the type repo, and require that namespace in all the rest of your code.

An example file that creates types
An example file that uses them

Here's an example stripped down to the essentials.

(ns my.types
  (:require [structural-typing.type :as type]
            [structural-typing.preds :as preds]
            ...)
  ;; I know it's unfashionable, but in this case a separate `use` is clearer than :refer :all
  (:use [structural-typing.type :exclude [built-like all-built-like
                                          <>built-like <>all-built-like
                                          built-like?]]))

(def type-repo
  (-> empty-type-repo
      (named :Point
             (requires :x :y)
             {:x integer? :y integer?})

      ...))

(def built-like (partial type/built-like type-repo))
(def all-built-like (partial type/all-built-like type-repo))
(def <>built-like (partial type/<>built-like type-repo))
(def <>all-built-like (partial type/<>all-built-like type-repo))
(def built-like? (partial type/built-like? type-repo))

Then use it from other namespaces:

(ns my.services
  (:require [my.types :as type])
  ...)

(defn frob [payload]
  (some-> (type/checked :Point payload)
          form-triangle
          ...))

... or, if you use the Either monad:

(defn frob [payload]
   (monad-let [validated (type/checked :Point payload)
               triangle (form-triangle validated)
               ...]
     (right ...)))

Tip: if you find yourself wanting more than one type repository, perhaps that's a sign your one project contains more than one bounded context. Perhaps you should have two projects?