What you have are not left and right entities, but two nodes defined in an identical way, and unfortunately you can't have two keys with the same name in the map, since spec does not allow "aliasing" of a keyword to a spec, but instead uses the keyword itself to identify the spec.
One option, if you are willing, is to define the left and right nodes in terms of a single ::children
key, which is a collection of (one or) two ::node
s.
(s/def ::key string?)
(s/def ::value string?)
(s/def ::n int?)
(s/def ::node (s/keys :req [::key ::value ::n]))
(s/def ::children (s/coll-of ::node :count 2))
;; for 1 or 2 children: (s/coll-of ::node :min-count 1 :max-count 2)
(s/valid? ::node
{::key "parent-1" ::value "parent-1" ::n 1
::children [{::key "leaf-1" ::value "leaf-1" ::n 2}
{::key "parent-2" ::value "parent-2" ::n 3
::children [{::key "leaf-2" ::value "leaf-2" ::n 4}
{::key "leaf-3" ::value "leaf-3" ::n 5}]}]})
This gives you a similar structure, with the slight additional complexity of a vector containing two nodes, instead of two keys, each with a node.
Another option that allows for a definition purely in terms of itself is to forego a map structure, and do a nested vector instead:
(s/def ::node (s/or :parent (s/coll-of ::node :count 2)
:leaf (s/tuple ::key ::value ::n)))
(s/valid? ::node
[[[["a" "a" 1]
["b" "b" 2]]
["c" "c" 3]]
["d" "d" 4]])
This works because the elements are sequential and need not be associated with a unique key, as in the map structure above (yes, vectors are associative also, but their sequential nature is being used in this case). This is admittedly not as "clean," and the first method is probably preferred, but it is an option if you're willing to forego the associative structure and trade it for a sequential one.
::node
isn't defined in the definitions of::left
, and::right
, so you may want to try defining::node
before those two. – Diamine::node
is defined in terms of::left
and::right
, which won't yet be defined. A circuit breaker is needed, likedeclare
in Clojure. – Unmanned::left
and::right
defined after::node
. See my answer for a pasted REPL session. – Baba