Fork me on GitHub
#clojure-spec
<
2021-04-18
>
mokr10:04:04

I got a bit carried away experimenting with specs and I believe I might have hit a dead end unless spec can actually pick apart a string (like I could do with regex capturing groups or Instaparse). Can spec accomplish something like this contrived example?

(s/def ::title #{"mr" "mrs" "dr" ,,,})
  (s/def ::surname #{"green" "brown" "who" ,,,})
  (s/def ::title-surname (s/__ :title ::title :surname ::surname))
  (s/conform ::title-surname "mrgreen")                     ;;alt1 => [:title "mr" :surname "green"]
  (s/conform ::title-surname "mrsbrown")                    ;;alt2 => {:title "mrs" :surname "brown"}

borkdude10:04:17

@mokr The standard answer to this is: although you could, spec isn't designed for parsing strings

mokr10:04:25

@borkdude Thanks, that settles it. I need to backtrack a bit.

joelittlejohn21:04:34

I agree with @borkdude, spec isn’t the right tool for this, however I have done this kind of thing in past for fun:

(s/def ::title
  (s/alt :mr (s/cat :m #{\m} :r #{\r})
         :mrs (s/cat :m #{\m} :r #{\r} :s #{\s})
         :dr (s/cat :d #{\d} :r #{\r})))
(s/def ::surname
  (s/alt :green (s/cat :g #{\g} :r #{\r} :e #{\e} :e #{\e} :n #{\n})
         :brown (s/cat :b #{\b} :r #{\r} :o #{\o} :w #{\w} :n #{\n})
         :who (s/cat :w #{\w} :h #{\h} :o #{\o})))
(s/def ::title-surname
  (s/cat :title ::title :surname ::surname))

(s/conform ::title-surname (seq "mrwho"))
;; => {:title [:mr {:m \m, :r \r}], :surname [:who {:w \w, :h \h, :o \o}]}

(s/conform ::title-surname (seq "mrsbrown"))
;; => {:title [:mrs {:m \m, :r \r, :s \s}], :surname [:brown {:b \b, :r \r, :o \o, :w \w, :n \n}]}

twashing22:04:30

Does cljs.spec.test.alpha/check generate bad input? I have a function spec that specifies a map for its input. gen/generate and gen/sample work fine. But calling cljs.spec.test.alpha/check fails with the error: More than one element found in structure. And it looks like the spec system is generating incorrect input. bar spec

(s/def ::check-run
  (s/keys
    :req-un
    [::action
     ::check_run
     ::installation
     ::organization
     ::repository
     ::sender]))
foo.cljs
(s/def ::payload :bar/check-run)
(s/def ::check-run-started (s/keys :req-un [::payload]))

(s/fdef check-run->cijob-created
  :args (s/cat :arg ::check-run-started))

(defn check-run->cijob-created [arg])
While the function spec only declares A, the spec system is generating B.
;; A
{:payload {:action "", :check_run {:html_url "", }}, ...}

;; B
[({:payload {:action "", :check_run {:html_url "", }}, ...}})]
workbench
(cljs.spec.test.alpha/check
  `foo/check-run->cijob-created
  {:clojure.spec.test.check/opts {:num-tests 10}})

[{:spec #object[cljs.spec.alpha.t_cljs$spec$alpha50916],
  :clojure.spec.test.check/ret
  {:shrunk
   {:total-nodes-visited 313, :depth 148, :pass? false, :result #object[Error Error: More than one element found in structure: 0], :result-data #:clojure.test.check.properties{:error #object[Error Error: More than one element found in structure: 0]}, :time-shrinking-ms 11299,
    :smallest
    [({:payload {:action "", :check_run {:html_url "", }}, ...}})]},
  :sym foo/check-run->cijob-created,
  :failure #object[Error Error: More than one element found in structure: 0]}]

twashing03:04:40

Ok, figured this one out. It was failing due to my use of a specter macro + navigator. I’m not sure how. But somehow this messes up test.check generators. https://github.com/redplanetlabs/specter/wiki/List-of-Macros#select-one

twashing03:04:29

I’m assuming it’s some kind of strange interplay that’s unworkable with Clojurescript’s macro system.