Skip to content

Instantly share code, notes, and snippets.

@camsaul
Created July 12, 2019 17:47
Show Gist options
  • Save camsaul/3239355409d18308c779ed9a320a7011 to your computer and use it in GitHub Desktop.
Save camsaul/3239355409d18308c779ed9a320a7011 to your computer and use it in GitHub Desktop.
Multimethod schemas
(def Apple
{::type (s/eq :apple)
:color (s/enum :red :yellow :green)})
(def Orange
{::type (s/eq :orange)
:color (s/eq :orange)})
(def Fruit
(s/conditional
#(= (::type %) :apple) Apple
#(= (::type %) :orange) Orange))
(defmulti fruit-schema
::type)
(defmethod fruit-schema :apple [_]
{::type (s/eq :apple)
:color (s/enum :red :yellow :green)})
(defmethod fruit-schema :orange [_]
{::type (s/eq :orange)
:color (s/eq :orange)})
(defrecord MultiSchema [multifn]
s/Schema)
(defn multi-schema [^clojure.lang.MultiFn multifn]
(reify
s/Schema
(spec [this]
(schema.spec.variant/variant-spec
;; precondition
(partial get-method multifn)
;; options
(for [[dispatch-value f] (methods multifn)]
{:guard #(= ((.dispatchFn multifn) %) dispatch-value)
:schema (f nil)})
;; error message function
(fn [_] (s/explain this))))
(explain [_]
(cons 'one-of (keys (methods multifn))))))
(def MultiFruit (multi-schema fruit-schema))
(s/check MultiFruit {::type :apple, :color :yellow})
;; -> nil
(s/check MultiFruit {::type :apple})
;; -> {:color missing-required-key}
(s/check MultiFruit {::type :strawberry})
;; -> (not (one-of :orange :apple))
(defmethod fruit-schema :banana [_]
{::type (s/eq :banana)
:ripe? s/Bool})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment