Skip to content

Instantly share code, notes, and snippets.

@aphyr
Last active May 26, 2017 02:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aphyr/0036979ac25f6506317737d97990d5cb to your computer and use it in GitHub Desktop.
Save aphyr/0036979ac25f6506317737d97990d5cb to your computer and use it in GitHub Desktop.
(def example-history
{:txns [{:ops [{:f :read, :k :x, :v 1}]}]})
(defn index-txns
"Takes a raw history, and adds a sequential integer index :i to each txn in the history."
[raw-history]
(update example-history :txns
(partial map-indexed (fn [i txn] (assoc txn :i i)))))
; So in core.typed I'd type this as (ignoring type functions and just writing things out literally)
(defalias Read (HMap :mandatory {:f :read, :k Any, :v Any}))
(defalias Write (HMap :mandatory {:f :write, :k Any, :v Any}))
(defalias Op (U Read Write))
(defalias Txn (HMap :mandatory {:ops (Vec Op)}))
(defalias IndexedTxn (I Txn (HMap :mandatory {:i Long})))
(defalias RawHistory (HMap :mandatory {:txns (Vec Txn)}))
(defalias IndexedHistory (HMap :mandatory {:txns (Vec IndexedTxn)}))
(ann index-txns [RawHistory -> IndexedHistory])
; I often write programs with several passes like `index-txns`, and many functions that use
; various intermediate types--perhaps depending on a particular index structure having been computed.
; I'd like the type system, or clojure.spec, to enforce correctness here--but it seems silly to pick
; new key names for every single interpretation of a history or txn. Clojure.spec seems to suggest
; that I choose new namespaced keys, like :my.project.history.indexed/txns,
; :my.project.history.raw/txns, etc, which I could... see working internally, but is both a.) clunky
; and b.) at odds with what the library as a whole is supposed to do, e.g, take and return maps with
; unqualified keywords with slightly different interpretations, owing, for example, to the introduction
; of new fields.
; What I really want, I suppose, is a clojure.spec equivalent for HMap.
@puredanger
Copy link

puredanger commented May 26, 2017

(s/def ::f #{:read :write})
(s/def ::k any?)
(s/def ::v any?)
(s/def ::op (s/keys :req-un [::f ::k ::v]))
(s/def ::ops (s/coll-of ::op :kind vector?))

(s/def ::txn (s/keys :req-un [::ops]))
(s/def ::i int?)
(s/def ::indexed-txn (s/keys :req-un [::ops ::i]))

(s/def ::txns (s/coll-of ::txn :kind vector?))
(s/def ::raw-history (s/keys :req-un [::txns]))
(s/def :indexed/txns (s/coll-of ::indexed-txn :kind vector?))
(s/def ::indexed-history (s/keys :req-un [:indexed/txns]))

(def example-history
  {:txns [{:ops [{:f :read, :k :x, :v 1}]}]})

(s/valid? ::raw-history example-history) ;; true

(def example-indexed-history
  {:txns [{:ops [{:f :read :k :x :v 1} {:f :write :k :y :v 2}] :i 1}]})

(s/valid? ::indexed-history example-indexed-history) ;; true

(s/explain ::indexed-history
  {:txns [{:ops [{:f :read :k :x :v 1} {:f :write :k :y :v 2}]}]})

;; In: [:txns 0] val: {:ops [{:f :read, :k :x, :v 1} {:f :write, :k :y, :v 2}]} 
;; fails spec: :clojure.test-clojure.spec/indexed-txn 
;; at: [:txns] predicate: (contains? % :i)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment