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

The structural-typing.type class contains both functions that create types and also those that check them. That's convenient for examples and playing around at the repo. 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.

A file that creates types
A 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?