├── CHANGELOG.md ├── LICENSE ├── README.md ├── project.clj ├── src └── circle │ └── schema_typer.clj └── test └── circle ├── schema_typer_def.clj └── schema_typer_test.clj /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.2.3 2 | 3 | - Move to core.typed 0.2.53 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' from 19 | a Contributor if it was added to the Program by such Contributor itself or 20 | anyone acting on such Contributor's behalf. Contributions do not include 21 | additions to the Program which: (i) are separate modules of software 22 | distributed in conjunction with the Program under their own license 23 | agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement, 34 | including all Contributors. 35 | 36 | 2. GRANT OF RIGHTS 37 | a) Subject to the terms of this Agreement, each Contributor hereby grants 38 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 39 | reproduce, prepare derivative works of, publicly display, publicly perform, 40 | distribute and sublicense the Contribution of such Contributor, if any, and 41 | such derivative works, in source code and object code form. 42 | b) Subject to the terms of this Agreement, each Contributor hereby grants 43 | Recipient a non-exclusive, worldwide, royalty-free patent license under 44 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 45 | transfer the Contribution of such Contributor, if any, in source code and 46 | object code form. This patent license shall apply to the combination of the 47 | Contribution and the Program if, at the time the Contribution is added by 48 | the Contributor, such addition of the Contribution causes such combination 49 | to be covered by the Licensed Patents. The patent license shall not apply 50 | to any other combinations which include the Contribution. No hardware per 51 | se is licensed hereunder. 52 | c) Recipient understands that although each Contributor grants the licenses to 53 | its Contributions set forth herein, no assurances are provided by any 54 | Contributor that the Program does not infringe the patent or other 55 | intellectual property rights of any other entity. Each Contributor 56 | disclaims any liability to Recipient for claims brought by any other entity 57 | based on infringement of intellectual property rights or otherwise. As a 58 | condition to exercising the rights and licenses granted hereunder, each 59 | Recipient hereby assumes sole responsibility to secure any other 60 | intellectual property rights needed, if any. For example, if a third party 61 | patent license is required to allow Recipient to distribute the Program, it 62 | is Recipient's responsibility to acquire that license before distributing 63 | the Program. 64 | d) Each Contributor represents that to its knowledge it has sufficient 65 | copyright rights in its Contribution, if any, to grant the copyright 66 | license set forth in this Agreement. 67 | 68 | 3. REQUIREMENTS 69 | 70 | A Contributor may choose to distribute the Program in object code form under its 71 | own license agreement, provided that: 72 | 73 | a) it complies with the terms and conditions of this Agreement; and 74 | b) its license agreement: 75 | i) effectively disclaims on behalf of all Contributors all warranties and 76 | conditions, express and implied, including warranties or conditions of 77 | title and non-infringement, and implied warranties or conditions of 78 | merchantability and fitness for a particular purpose; 79 | ii) effectively excludes on behalf of all Contributors all liability for 80 | damages, including direct, indirect, special, incidental and 81 | consequential damages, such as lost profits; 82 | iii) states that any provisions which differ from this Agreement are offered 83 | by that Contributor alone and not by any other party; and 84 | iv) states that source code for the Program is available from such 85 | Contributor, and informs licensees how to obtain it in a reasonable 86 | manner on or through a medium customarily used for software exchange. 87 | 88 | When the Program is made available in source code form: 89 | 90 | a) it must be made available under this Agreement; and 91 | b) a copy of this Agreement must be included with each copy of the Program. 92 | Contributors may not remove or alter any copyright notices contained within 93 | the Program. 94 | 95 | Each Contributor must identify itself as the originator of its Contribution, if 96 | any, in a manner that reasonably allows subsequent Recipients to identify the 97 | originator of the Contribution. 98 | 99 | 4. COMMERCIAL DISTRIBUTION 100 | 101 | Commercial distributors of software may accept certain responsibilities with 102 | respect to end users, business partners and the like. While this license is 103 | intended to facilitate the commercial use of the Program, the Contributor who 104 | includes the Program in a commercial product offering should do so in a manner 105 | which does not create potential liability for other Contributors. Therefore, if 106 | a Contributor includes the Program in a commercial product offering, such 107 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 108 | every other Contributor ("Indemnified Contributor") against any losses, damages 109 | and costs (collectively "Losses") arising from claims, lawsuits and other legal 110 | actions brought by a third party against the Indemnified Contributor to the 111 | extent caused by the acts or omissions of such Commercial Contributor in 112 | connection with its distribution of the Program in a commercial product 113 | offering. The obligations in this section do not apply to any claims or Losses 114 | relating to any actual or alleged intellectual property infringement. In order 115 | to qualify, an Indemnified Contributor must: a) promptly notify the Commercial 116 | Contributor in writing of such claim, and b) allow the Commercial Contributor to 117 | control, and cooperate with the Commercial Contributor in, the defense and any 118 | related settlement negotiations. The Indemnified Contributor may participate in 119 | any such claim at its own expense. 120 | 121 | For example, a Contributor might include the Program in a commercial product 122 | offering, Product X. That Contributor is then a Commercial Contributor. If that 123 | Commercial Contributor then makes performance claims, or offers warranties 124 | related to Product X, those performance claims and warranties are such 125 | Commercial Contributor's responsibility alone. Under this section, the 126 | Commercial Contributor would have to defend claims against the other 127 | Contributors related to those performance claims and warranties, and if a court 128 | requires any other Contributor to pay any damages as a result, the Commercial 129 | Contributor must pay those damages. 130 | 131 | 5. NO WARRANTY 132 | 133 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 134 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 135 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 136 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 137 | Recipient is solely responsible for determining the appropriateness of using and 138 | distributing the Program and assumes all risks associated with its exercise of 139 | rights under this Agreement , including but not limited to the risks and costs 140 | of program errors, compliance with applicable laws, damage to or loss of data, 141 | programs or equipment, and unavailability or interruption of operations. 142 | 143 | 6. DISCLAIMER OF LIABILITY 144 | 145 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 146 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 147 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 148 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 149 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 150 | OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 151 | GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 152 | 153 | 7. GENERAL 154 | 155 | If any provision of this Agreement is invalid or unenforceable under applicable 156 | law, it shall not affect the validity or enforceability of the remainder of the 157 | terms of this Agreement, and without further action by the parties hereto, such 158 | provision shall be reformed to the minimum extent necessary to make such 159 | provision valid and enforceable. 160 | 161 | If Recipient institutes patent litigation against any entity (including a 162 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 163 | (excluding combinations of the Program with other software or hardware) 164 | infringes such Recipient's patent(s), then such Recipient's rights granted under 165 | Section 2(b) shall terminate as of the date such litigation is filed. 166 | 167 | All Recipient's rights under this Agreement shall terminate if it fails to 168 | comply with any of the material terms or conditions of this Agreement and does 169 | not cure such failure in a reasonable period of time after becoming aware of 170 | such noncompliance. If all Recipient's rights under this Agreement terminate, 171 | Recipient agrees to cease use and distribution of the Program as soon as 172 | reasonably practicable. However, Recipient's obligations under this Agreement 173 | and any licenses granted by Recipient relating to the Program shall continue and 174 | survive. 175 | 176 | Everyone is permitted to copy and distribute copies of this Agreement, but in 177 | order to avoid inconsistency the Agreement is copyrighted and may only be 178 | modified in the following manner. The Agreement Steward reserves the right to 179 | publish new versions (including revisions) of this Agreement from time to time. 180 | No one other than the Agreement Steward has the right to modify this Agreement. 181 | The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation 182 | may assign the responsibility to serve as the Agreement Steward to a suitable 183 | separate entity. Each new version of the Agreement will be given a 184 | distinguishing version number. The Program (including Contributions) may always 185 | be distributed subject to the version of the Agreement under which it was 186 | received. In addition, after a new version of the Agreement is published, 187 | Contributor may elect to distribute the Program (including its Contributions) 188 | under the new version. Except as expressly stated in Sections 2(a) and 2(b) 189 | above, Recipient receives no rights or licenses to the intellectual property of 190 | any Contributor under this Agreement, whether expressly, by implication, 191 | estoppel or otherwise. All rights in the Program not expressly granted under 192 | this Agreement are reserved. 193 | 194 | This Agreement is governed by the laws of the State of New York and the 195 | intellectual property laws of the United States of America. No party to this 196 | Agreement will bring a legal action under this Agreement more than one year 197 | after the cause of action arose. Each party waives its rights to a jury trial in 198 | any resulting litigation. 199 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | schema-typer 2 | ============ 3 | 4 | [![CircleCI Status](https://circleci.com/gh/circleci/schema-typer.png?style=badge)](https://circleci.com/gh/circleci/schema-typer) 5 | 6 | Creates core.typed types from prismatic/schema definitions. Inspired by https://gist.github.com/c-spencer/6569571 7 | 8 | ```clojure 9 | [circleci/schema-typer "0.2.3"] 10 | ``` 11 | Requires schema > 0.2.0 12 | 13 | 14 | Motivation 15 | ========== 16 | Q: Why the hell build this? 17 | 18 | A: core.typed is good at compile-time validation. prismatic/schema is 19 | good at run-time validation. schema-typer avoids duplication between your 20 | core.type definition, and your schema definition. Having types for 21 | schemas allows you to prove at compile-time that validation is 22 | happening, at runtime. 23 | 24 | Usage 25 | ===== 26 | ```clojure 27 | (:require [schema.core :as s] 28 | [circle.schema-typer :refer (def-schema-alias def-validator)]) 29 | 30 | (def user-schema {:login String}) ;; define a normal prismatic schema 31 | 32 | (def-schema-alias User user-schema) 33 | ;; defines (def-alias User (HMap :mandatory {:login String})) 34 | 35 | (def-validator validate-user User user-schema) 36 | ;; defines 37 | ;; (t/ann validate-user [Any -> User]) 38 | ;; (defn validate-user [x] 39 | ;; (s/validate user-schema x)) 40 | ``` 41 | 42 | Limitations 43 | =========== 44 | 45 | It's pretty janky atm. There are a few critical ^:no-checks, because 46 | core.typed doesn't yet 'believe' that schema is validating 47 | properly. Currently, def-validators are ^:no-checked, but they do call (schema.core/validate schema) so you get proper checking at runtime. 48 | 49 | The mapping from schema -> core.typed is currently extremely incomplete, but it should be obvious how to extend. Patches welcome. 50 | 51 | License 52 | ======= 53 | EPL 1.0, the same as Clojure 54 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject circleci/schema-typer "0.2.3" 2 | :dependencies [[org.clojure/core.typed "0.2.53"] 3 | [prismatic/schema "0.2.1"]] 4 | :profiles {:dev {:dependencies [[org.clojure/clojure "1.5.1"]]}}) 5 | -------------------------------------------------------------------------------- /src/circle/schema_typer.clj: -------------------------------------------------------------------------------- 1 | (ns circle.schema-typer 2 | "Creates core.typed types from prismatic/schema 3 | definitions. Inspired by https://gist.github.com/c-spencer/6569571" 4 | (:import (clojure.lang Keyword IMapEntry)) 5 | (:require [clojure.core.typed :as t] 6 | [schema.core :as s])) 7 | 8 | (t/warn-on-unannotated-vars) 9 | 10 | (t/ann clojure.core/group-by (t/All [x y] [(t/IFn [x -> y]) (t/Seq x) -> (t/Map y (t/Seq x))])) 11 | 12 | ;; refine these later 13 | (t/defalias Schema (t/U Number (t/Map t/Any t/Any) (t/HMap))) 14 | (t/defalias CoreType (t/Rec [x] (t/U t/Sym (t/Seq (t/U t/Sym t/Kw x))))) 15 | 16 | (t/ann schema.core/validate [Schema t/Any -> t/Any]) 17 | 18 | (defn convert-predicate-dispatch [s] 19 | (:pred-name s)) 20 | 21 | (defmulti convert-predicate "" #'convert-predicate-dispatch) 22 | 23 | (defmethod convert-predicate 'keyword? [s] 24 | 'clojure.lang.Keyword) 25 | 26 | (t/ann convert-dispath [Schema -> Class]) 27 | (defn convert-dispatch [schema] 28 | (cond 29 | (= schema.core.Predicate (class schema)) :predicate 30 | (class? schema) schema 31 | :else (class schema))) 32 | 33 | (t/ann ^:no-check convert [Schema -> CoreType]) 34 | (defmulti convert "" #'convert-dispatch) 35 | 36 | (defmethod convert :predicate [s] 37 | (convert-predicate s)) 38 | 39 | (defmethod convert Number [s] 40 | 'java.lang.Number) 41 | 42 | (defmethod convert String [s] 43 | 'java.lang.String) 44 | 45 | (defmethod convert Keyword [s] 46 | 'clojure.lang.Keyword) 47 | 48 | (defmethod convert Boolean [s] 49 | 'java.lang.Boolean) 50 | 51 | (defmethod convert schema.core.AnythingSchema [s] 52 | `t/Any) 53 | 54 | (defmethod convert clojure.lang.Symbol [s] 55 | s) 56 | 57 | (t/ann hmap-grouper [(IMapEntry t/Any t/Any) -> (t/U (t/Val :mandatory) 58 | (t/Val :optional))]) 59 | (defn hmap-grouper 60 | [kv] 61 | (if (= schema.core.OptionalKey (class (key kv))) 62 | :optional 63 | :mandatory)) 64 | 65 | (t/ann convert-map [(t/Map Schema Schema) -> CoreType]) 66 | (defn convert-map [s] 67 | (assert (map? s)) 68 | (assert (= 1 (count s)) "convert-map only supports one kv") 69 | (let [ks (-> s first key) 70 | vs (-> s first val) 71 | kt (convert ks) 72 | vt (convert vs)] 73 | (list 'clojure.core.typed/Map kt vt))) 74 | 75 | (t/ann ^:no-check convert-hmap [(t/HMap) -> CoreType]) 76 | (defn convert-hmap 77 | "Returns a core.typed HMap." 78 | [s] 79 | (assert (map? s)) 80 | (let [{:keys [mandatory optional]} (group-by hmap-grouper s) 81 | ;; strip the schema.core.OptionalKey off optionals 82 | optional (map (fn [[k v]] 83 | [(:k k) v]) optional) 84 | convert-kvs (fn [kvs] 85 | (->> 86 | (for [[k v] kvs] 87 | (do 88 | [k (convert v)])) 89 | (into {})))] 90 | (list `t/HMap 91 | :mandatory (convert-kvs mandatory) 92 | :optional (convert-kvs optional)))) 93 | 94 | (defn hmap-key? 95 | "True if this key is a valid pure-hmap key, i.e. Keyword or OptionalKey " 96 | [k] 97 | (or (keyword? k) 98 | (= schema.core.OptionalKey (class k)))) 99 | 100 | (defn pure-map? [s] 101 | (and (map? s) 102 | (every? (comp not hmap-key?) (keys s)))) 103 | 104 | (defn pure-hmap? [s] 105 | (and (map? s) 106 | (every? hmap-key? (keys s)))) 107 | 108 | (defn intersection [t1 t2] 109 | (list `t/I t1 t2)) 110 | 111 | (defn split-map 112 | [s] 113 | "given a schema that contains HMap and t/Map keys, split and return two maps" 114 | (let [{hmap true 115 | map false} (group-by (fn [[k v]] 116 | (hmap-key? k)) s)] 117 | {:hmap (into {} hmap) 118 | :map (into {} map)})) 119 | 120 | (defmethod convert clojure.lang.IPersistentMap [s] 121 | 122 | ;; three kinds of maps: 123 | ;; 'pure' map {Keyword String} -> (t/Map Keyword String) 124 | ;; 'pure' hmap: {:foo String} -> (HMap :mandatory {:foo String}) 125 | ;; 'mixed': {:foo String, Keyword String} -> (I (t/Map Keyword STring) (HMap :mandatory {:foo String} 126 | 127 | (assert (or (map? s) (class? s))) 128 | (cond 129 | (= s clojure.lang.IPersistentMap) '(clojure.core.typed/Map Any Any) 130 | (pure-map? s) (convert-map s) 131 | (pure-hmap? s) (convert-hmap s) 132 | :else (let [{:keys [hmap map]} (split-map s)] 133 | (intersection (convert-hmap hmap) (convert-map map))))) 134 | 135 | (defmethod convert clojure.lang.IPersistentVector [s] 136 | (assert (= 1 (count s))) 137 | (list 'clojure.core.typed/Vec (convert (first s)))) 138 | 139 | (defmethod convert schema.core.Maybe [s] 140 | (list 'clojure.core.typed/Option (convert (:schema s)))) 141 | 142 | (t/ann schema->type [Schema -> CoreType]) 143 | (defn schema->type 144 | "Takes a prismatic schema. Returns a list of symbols that can be understood as a core.typed type." 145 | [s] 146 | (convert s)) 147 | 148 | (defmacro def-schema-alias 149 | "creates a defalias named type-name, from schema type" 150 | [type-name s] 151 | (let [s (eval s) ;; TODO can this be removed? 152 | concrete-type (schema->type s)] 153 | `(t/defalias ~type-name ~concrete-type))) 154 | 155 | (defmacro def-validator 156 | "defns a fn of type [Any -> type] that throws on validation failure. type should be a defalias created by def-schema-alias" 157 | [validator-name type schema] 158 | `(do 159 | (t/ann ~(vary-meta validator-name assoc :no-check true) [t/Any :-> ~type]) 160 | (defn ~validator-name [x#] 161 | (s/validate ~schema x#)))) 162 | -------------------------------------------------------------------------------- /test/circle/schema_typer_def.clj: -------------------------------------------------------------------------------- 1 | (ns circle.schema-typer-def 2 | (:require [clojure.core.typed :as t] 3 | [schema.core :as s] 4 | [circle.schema-typer :as st])) 5 | 6 | ;; data used for testing 7 | (t/ann user-schema st/Schema) 8 | (def user-schema {:login String 9 | :login-count Number}) 10 | 11 | (st/def-schema-alias User user-schema) 12 | (st/def-validator validate-user User user-schema) 13 | 14 | (t/ann foo [-> User]) 15 | (defn foo [] 16 | (validate-user {:login "arohner"})) 17 | -------------------------------------------------------------------------------- /test/circle/schema_typer_test.clj: -------------------------------------------------------------------------------- 1 | (ns circle.schema-typer-test 2 | (:import (clojure.lang Keyword 3 | IPersistentMap)) 4 | (:require [clojure.test :refer :all] 5 | [clojure.core.typed :as t] 6 | [schema.core :as s] 7 | [circle.schema-typer :as st])) 8 | 9 | (defn is-equiv 10 | "Takes a value, a schema and a *quoted* type, i.e. '(HMap :mandatory ...). Asserts that v passes both schema and type" 11 | [v s t] 12 | (is (s/validate s v)) 13 | (is (t/check-form* v t)) 14 | (is (t/check-form* v (st/schema->type s)))) 15 | 16 | (deftest numbers-work 17 | (is-equiv 3 s/Num 'java.lang.Number)) 18 | 19 | (deftest any 20 | (is-equiv "foo" s/Any `t/Any)) 21 | 22 | (deftest schema-pred 23 | (is-equiv :foo s/Keyword 'clojure.lang.Keyword)) 24 | 25 | (deftest hmaps-work 26 | (let [v {:foo 5} 27 | s {:foo Number} 28 | t `(t/HMap :mandatory {:foo java.lang.Number})] 29 | (is-equiv v s t))) 30 | 31 | (deftest maps-work 32 | (let [v {:bar "foo"} 33 | s {Keyword String} 34 | t `(t/Map clojure.lang.Keyword java.lang.String)] 35 | (is-equiv v s t))) 36 | 37 | (deftest nested-hmaps-work 38 | (let [v {:foo {:bar "baz"}} 39 | s {:foo {:bar String}} 40 | t `(t/HMap :mandatory {:foo (t/HMap :mandatory {:bar java.lang.String})})] 41 | (is-equiv v s t))) 42 | 43 | (deftest mixed-map-hmap 44 | (let [v {:foo {:bar "baz"} 45 | :not-listed "String"} 46 | s {:foo {:bar String} 47 | s/Keyword s/Any} 48 | t `(t/HMap :mandatory {:foo (t/HMap :mandatory {:bar java.lang.String})})] 49 | (is-equiv v s t))) 50 | 51 | (deftest map-class 52 | (let [v {:foo {:bar "baz"}} 53 | s {:foo IPersistentMap} 54 | t `(t/HMap :mandatory {:foo (t/Map t/Any t/Any)})] 55 | (is-equiv v s t))) 56 | 57 | (deftest optional-keys 58 | (let [s {:foo Number 59 | (s/optional-key :bar) String} 60 | t `(t/HMap :mandatory {:foo java.lang.Number} :optional {:bar java.lang.String})] 61 | (is-equiv {:foo 3} s t) 62 | (is-equiv {:foo 3 63 | :bar "hello"} s t))) 64 | 65 | (deftest maybe-value 66 | (let [s {:foo Number 67 | :bar (s/maybe String)} 68 | t `(t/HMap :mandatory {:foo java.lang.Number 69 | :bar (t/Option java.lang.String)})] 70 | (is-equiv {:foo 3 :bar nil} s t) 71 | (is-equiv {:foo 3 72 | :bar "hello"} s t))) 73 | 74 | (deftest vectors 75 | (let [s [{:foo Long}] 76 | t `(t/Vec (t/HMap :mandatory {:foo Integer}))] 77 | (is-equiv [{:foo 3}] s t))) 78 | 79 | (deftest bools 80 | (let [s {:foo s/Bool 81 | :bar Boolean} 82 | t `(t/HMap :mandatory {:foo java.lang.Boolean 83 | :bar java.lang.Boolean})] 84 | (is-equiv {:foo true :bar false} s t))) 85 | 86 | (deftest real-use-works 87 | (is (t/check-ns 'circle.schema-typer-def))) 88 | --------------------------------------------------------------------------------