Recursive clojure.spec doesn't work

668 views
Skip to first unread message

Johan Jonasson

unread,
Jul 20, 2016, 11:38:41 AM7/20/16
to Clojure
I might have stumbled upon a bug in clojure.spec, while trying to define a spec. This doesn't compile:

(s/def :html/element
  (s/cat
   :tag keyword?
   :attrs map?
   :children (s/* (s/alt :element (s/spec :html/element)
                         :string  string?))))

The exception says: "Unable to resolve spec: :html/element".

Is it a bug, or is there another way to define this?

Alex Miller

unread,
Jul 20, 2016, 11:46:20 AM7/20/16
to Clojure
s/spec is going to try to resolve that keyword during the definition. Does this do what you want?

(s/def :html/element
  (s/spec (s/cat
                 :tag keyword?
                 :attrs map?
                 :children (s/* (s/alt :element :html/element 
                                               :string string?)))))

Johan Jonasson

unread,
Jul 20, 2016, 2:12:44 PM7/20/16
to Clojure
It works! Thanks! 

Seems like I have to learn more about s/spec.

Aaron

unread,
Oct 5, 2016, 11:09:59 PM10/5/16
to Clojure
I was playing around with something similar to the OP and encountered this same problem using s/spec recursively. Yes, I get it that there are work-arounds, but it seems like this is a legitimate issue. s/spec as you say resolves at definition time, but the rest of the combinators (s/alt, s/and, s/keys, etc.) as far as I can tell are late binding. What's the rationale for s/spec behaving differently? If specs happen to get redefined later this could further complicate matters as everything defined with s/spec will reference the older spec. Why not just make s/spec late binding too and then there would be no such issue with recursive specs?

Alex Miller

unread,
Oct 6, 2016, 7:43:14 AM10/6/16
to Clojure
Can you share a specific case?

Aaron

unread,
Oct 6, 2016, 2:52:07 PM10/6/16
to Clojure
Well I'm referring to the OP's original example:

(s/def :html/element
  (s/cat
   :tag keyword?
   :attrs map?
   :children (s/* (s/alt :element (s/spec :html/element)
                         :string  string?))))

The exception says: "Unable to resolve spec: :html/element".

Yehonathan Sharvit

unread,
Oct 7, 2016, 1:13:12 AM10/7/16
to Clojure
This recursive definition also works - the difference is that the inner :html/element is not wrapped in s/spec:

(s/def :html/element
  (s/cat
    :tag keyword?
    :attrs map?
    :children (s/* (s/alt :element :html/element
                          :string  string?))))

I'm a bit confused about when recursion works and when it doesn't
Reply all
Reply to author
Forward
0 new messages