clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
Jim Newton 2020-11-10T16:35:17.333800Z

I'm getting an error from spec that I don't understand. Maybe someone can help me. The following example, which I've based on an example in https://clojure.org/guides/spec, works fine, and valid? returns true.

(s/valid? (s/keys :req [::first-name ::last-name ::email]
                  :opt [::phone])
          {::first-name "Bugs"
           ::last-name "Bunny"
           ::email "<mailto:bugs@example.com|bugs@example.com>"})
However, when I've programmatically generated the following list
(let [sss '(clojure.spec.alpha/keys
            :req [:clojure-rte.genus-spec-test/first-name
                  :clojure-rte.genus-spec-test/last-name
                  :clojure-rte.genus-spec-test/email]
            :opt [:clojure-rte.genus-spec-test/phone])]
  (s/valid? sss
          {::first-name "Bugs"
           ::last-name "Bunny"
           ::email "<mailto:bugs@example.com|bugs@example.com>"}))
I get an uninformative exception
Execution error (ClassCastException) at (REPL:1).
null
is there a way to all s/valid? with a programmatically generated spec?

borkdude 2020-11-10T16:37:07.334700Z

@jimka.issy An s-expression is not yet a spec. I would assume most people would write a macro for creating specs programmatically

alexmiller 2020-11-10T16:37:15.335100Z

Yes, but you need to understand more about how spec works

alexmiller 2020-11-10T16:37:42.335600Z

valid? works on spec objects

alexmiller 2020-11-10T16:38:01.336100Z

Spec forms need to be evaluated into spec objects first

alexmiller 2020-11-10T16:38:53.336500Z

In the case above, you could do that with eval

borkdude 2020-11-10T16:39:07.336800Z

or generate the form with a macro

alexmiller 2020-11-10T16:39:58.337800Z

Even so, you’re still leaning on eval

borkdude 2020-11-10T16:40:08.338100Z

yep

borkdude 2020-11-10T16:41:44.338900Z

Made some fun macros the other day:

(def kws (map keyword (repeatedly gensym)))

(defmacro cat [&amp; preds]
  `(clojure.spec.alpha/cat ~@(interleave kws
                                         (map expand-query preds))))
This allows you to write (g/cat int? string?), when you're not interested in the conformed value

alexmiller 2020-11-10T16:43:07.339500Z

Spec 2 has a lot more tools for all this stuff

borkdude 2020-11-10T16:43:20.339900Z

defop right?

alexmiller 2020-11-10T16:43:29.340200Z

That’s one

borkdude 2020-11-10T16:43:31.340300Z

any docs I could read yet?

alexmiller 2020-11-10T16:43:54.340700Z

The wiki pages if you haven’t seen those

alexmiller 2020-11-10T16:44:30.341500Z

But not everything is in there and it’s missing a lot of the internal design

borkdude 2020-11-10T16:44:39.341900Z

I'll just wait, no hurry

alexmiller 2020-11-10T16:44:53.342400Z

There is now an intermediate map data form now that you can work on directly

👏 1
alexmiller 2020-11-10T16:45:05.342900Z

But will almost certainly change

borkdude 2020-11-10T16:45:18.343200Z

Working with maps is a lot easier than writing macros (since macros beget more macros)

alexmiller 2020-11-10T16:46:34.344100Z

Some day rich or I will do a talk about the internals - there are several interesting aspects of it

👍 2
💯 1
borkdude 2020-11-10T16:47:49.344300Z

Looking forward

➕ 5
Jim Newton 2020-11-10T16:52:49.344800Z

macros wont help in this case. the origin of my spec is not sitting in an evaluation position.

alexmiller 2020-11-10T17:04:04.345Z

thus, eval

alexmiller 2020-11-10T17:04:19.345300Z

evaluating a spec form gives you a spec object