* Protocol/interop fixes to run metosin/malli Bringing up malli (schema validation) surfaced a batch of protocol and host-interop gaps. m/validate now works across the schema vocabulary (predicates, :map incl. nested/optional, :vector, :tuple, :enum, :maybe, :and, bounded int/string). - extend-type and reify now accept MULTIPLE protocols in one form (each bare symbol switches the current protocol). reify records every protocol it implements, so instance?/satisfies? recognise all of them. - Protocol method params support destructuring: reify/extend-type/deftype/ defrecord emit (fn ...) (which desugars patterns) instead of raw fn*. - instance? of a PROTOCOL works like satisfies? for reify/record instances, matching short names across qualified/bare protocol references. - @x reads as the qualified clojure.core/deref, so it still derefs where a ns excludes and rebinds deref (malli does). Updated reader-test + the reader spec/grammar (S11, deref rule). - Java collection interop on jolt collections: .nth/.count/.valAt/.get/.seq/ .containsKey route to the clojure.core equivalent (1-arg and 0-arg paths). - java.util.HashMap capacity/load-factor constructors + .putAll. - A class used as a value resolves to its instances' type, so Pattern -> the regex type (malli keys class-schemas by it). - Shims for malli's load path: LazilyPersistentVector/createOwning and PersistentArrayMap/createWithCheck statics. m/explain not yet working (jolt-fjb1). Full gate green. * satisfies? recognizes reify, consistent with instance? A reify's protocol methods are instance-local, so they aren't in the global type registry that type-satisfies? consults — satisfies? returned false for a reify even when it implemented the protocol. Check the protocols the reify records on itself (the same :jolt/protocols list instance? uses), matching short names like instance? does. Covers single- and multi-protocol reify. --------- Co-authored-by: Yogthos <yogthos@gmail.com>
64 lines
3.3 KiB
Text
64 lines
3.3 KiB
Text
# Specification: protocols, types and records.
|
|
(use ../support/harness)
|
|
|
|
(defspec "protocols / defprotocol & dispatch"
|
|
["protocol on record" "16"
|
|
"(do (defprotocol Shape (area [s])) (defrecord Sq [side] Shape (area [_] (* side side))) (area (->Sq 4)))"]
|
|
["protocol on deftype" "16"
|
|
"(do (defprotocol Shape (area [s])) (deftype Sq [side] Shape (area [_] (* side side))) (area (->Sq 4)))"]
|
|
["multiple methods" "[1 2]"
|
|
"(do (defprotocol P (m [s]) (n [s])) (defrecord R [a b] P (m [_] a) (n [_] b)) [(m (->R 1 2)) (n (->R 1 2))])"]
|
|
["multiple protocols" "[:a :b]"
|
|
"(do (defprotocol P1 (p1 [s])) (defprotocol P2 (p2 [s])) (deftype T [] P1 (p1 [_] :a) P2 (p2 [_] :b)) [(p1 (->T)) (p2 (->T))])"]
|
|
["method args" "7"
|
|
"(do (defprotocol P (add [s x])) (defrecord R [n] P (add [_ x] (+ n x))) (add (->R 5) 2))"]
|
|
["extend-type" "10"
|
|
"(do (defprotocol P (twice [s])) (extend-type Number P (twice [n] (* n 2))) (twice 5))"]
|
|
["extend-protocol" "[2 4]"
|
|
"(do (defprotocol P (dbl [s])) (extend-protocol P Number (dbl [n] (* n 2))) [(dbl 1) (dbl 2)])"])
|
|
|
|
(defspec "protocols / records"
|
|
["record field access" "1"
|
|
"(do (defrecord R [a b]) (:a (->R 1 2)))"]
|
|
["record map access" "2"
|
|
"(do (defrecord R [a b]) (get (->R 1 2) :b))"]
|
|
["record equality" "true"
|
|
"(do (defrecord R [a b]) (= (->R 1 2) (->R 1 2)))"]
|
|
["record inequality" "false"
|
|
"(do (defrecord R [a b]) (= (->R 1 2) (->R 3 4)))"]
|
|
["map-> factory" "1"
|
|
"(do (defrecord R [a b]) (:a (map->R {:a 1 :b 2})))"]
|
|
["record? true" "true"
|
|
"(do (defrecord R [a]) (record? (->R 1)))"]
|
|
["assoc on record" "9"
|
|
"(do (defrecord R [a]) (:a (assoc (->R 1) :a 9)))"])
|
|
|
|
(defspec "protocols / reify & satisfies"
|
|
["reify dispatch" "42"
|
|
"(do (defprotocol P (m [_])) (m (reify P (m [_] 42))))"]
|
|
["reify multi-method" "[1 2]"
|
|
"(do (defprotocol P (a [_]) (b [_])) (let [r (reify P (a [_] 1) (b [_] 2))] [(a r) (b r)]))"]
|
|
["satisfies? true" "true"
|
|
"(do (defprotocol P (m [_])) (defrecord R [] P (m [_] 1)) (satisfies? P (->R)))"]
|
|
["satisfies? false" "false"
|
|
"(do (defprotocol P (m [_])) (defrecord R []) (satisfies? P (->R)))"]
|
|
["satisfies? reify" "true"
|
|
"(do (defprotocol P (m [_])) (satisfies? P (reify P (m [_] 1))))"]
|
|
["satisfies? reify multi-protocol" "[true true]"
|
|
"(do (defprotocol P (m [_])) (defprotocol Q (n [_])) (let [r (reify P (m [_] 1) Q (n [_] 2))] [(satisfies? P r) (satisfies? Q r)]))"]
|
|
["satisfies? reify other proto" "false"
|
|
"(do (defprotocol P (m [_])) (defprotocol Q (n [_])) (satisfies? Q (reify P (m [_] 1))))"]
|
|
["instance? record" "true"
|
|
"(do (defrecord R [a]) (instance? R (->R 1)))"]
|
|
["dot constructor" "5"
|
|
"(do (deftype P [n]) (.-n (P. 5)))"]
|
|
["dot ctor + method" "5"
|
|
"(do (defprotocol G (val-of [_])) (deftype P [n] G (val-of [_] n)) (val-of (P. 5)))"])
|
|
|
|
# defprotocol accepts Clojure's optional docstring + leading keyword options
|
|
# before the signatures (honeysql: :extend-via-metadata true).
|
|
(defspec "protocols / defprotocol options"
|
|
["docstring + option + method" ":hi"
|
|
"(do (defprotocol Pdoc \"docs\" :extend-via-metadata true (greet [x])) (extend-protocol Pdoc String (greet [s] :hi)) (greet \"x\"))"]
|
|
["option only" "3"
|
|
"(do (defprotocol Popt :extend-via-metadata true (plus2 [x])) (extend-protocol Popt Long (plus2 [n] (+ n 2))) (plus2 1))"])
|