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
Alex 2020-04-21T01:12:03.209700Z

I'm having some trouble with adding a spec for a keyword which is supposed to represent a function. I tried this

(s/fdef ::on-change
  :args (s/cat :value :option/id))

(s/def ::props (s/keys :req-un [::options
                                ::value]
                       :opt-un [::class
                                ::centered?
                                ::on-change
                                ::variant]))

(s/fdef tabs
  :args (s/cat :props ::props))

Alex 2020-04-21T01:12:20.210Z

> Var clojure.test.check.properties/for-all* does not exist, clojure.test.check.properties never required

Alex 2020-04-21T01:12:54.210500Z

from trying to pass ::on-change to fdef

Alex 2020-04-21T01:14:43.212400Z

After reading the API docs, it seems fdef can only take a symbol naming a function. My question is... how can I create a spec for the function that is passed as :on-change?

robertfw 2020-04-21T01:17:52.214700Z

It's funny you ask, I was just arriving to ask something similar. I have a spec describing a map, of which some keys are functions. I'd love to be able to specify that those functions should conform to a given fdef, but not sure how to accomplish that. As is, I just have the keys referring to the functions specced using fn?

Alex 2020-04-21T01:19:35.215400Z

Now I don't feel too silly asking 🙂

alexmiller 2020-04-21T01:26:02.215700Z

use ifn? not fn?

alexmiller 2020-04-21T01:27:20.216300Z

you can use fspec to do this as well, but there are some big caveats for generation

alexmiller 2020-04-21T01:27:43.216700Z

personally, I have not found it to be worth doing fspec over ifn?

Alex 2020-04-21T01:32:36.218100Z

@alexmiller Does ifn? allow you to specify the shape of the arguments that are passed in? I can't seem to find any examples doing so

alexmiller 2020-04-21T01:37:39.218500Z

no

alexmiller 2020-04-21T01:38:17.218900Z

you can do that with fspec (same args as fdef)

Alex 2020-04-21T01:45:23.219600Z

Tried a simple fspec but that doesn't seem to compile. Hmm

(s/fspec ::on-change
         :args (s/cat :value number?)
         :ret any?)

sgepigon 2020-04-21T11:40:35.225400Z

(s/def ::on-change
  (s/fspec :args (s/cat :value number?)
           :ret any?))

Alex 2020-04-21T14:09:06.228900Z

I get this error when I try that. Is this expected? > Uncaught Error: Var clojure.test.check.properties/for-all* does not exist, clojure.test.check.properties never required

kenny 2020-04-21T14:09:43.229100Z

fspec uses test.check to ensure the correctness of the spec'ed input.

Alex 2020-04-21T23:10:08.234100Z

Thank you!

kenny 2020-04-21T02:37:14.219700Z

Remove the first arg:

(s/fspec :args (s/cat :value number?)
         :ret any?)

Alex 2020-04-21T03:06:43.221400Z

Thanks! How do I associate it to the keyword which will use the spec?

Aron 2020-04-21T07:23:13.221900Z

how can I spec transient things like values of let bindings?

Aron 2020-04-21T07:33:05.222700Z

I see the suggestion to use s/assert in docs, but isn't that also something that I should only do in development and not make it part of the code that might be shipped eventually?

Ben Sless 2020-04-21T09:02:03.223900Z

Is it possible to spec/validate java collections? this naive example doesn't work

(s/def ::foo int?)
(s/def ::bar string?)
(s/def ::m (s/keys :req [::foo ::bar]))

user=> (s/valid? ::m {::foo 1 ::bar "2"})
true
user=> (s/valid? ::m (java.util.HashMap. {::foo 1 ::bar "2"}))
false

Ben Sless 2020-04-21T09:06:37.224Z

It might be terrible but I recently had a similar issue, went for this solution:

(let [x (expr ...)]
  (eval
   `(s/def ::my-spec
      (s/keys
       :req-un
       [~x]))))

Aron 2020-04-21T09:06:50.224200Z

😄

Aron 2020-04-21T09:07:00.224400Z

i am not sure about terrible but it's scary

Ben Sless 2020-04-21T09:07:58.224600Z

it is. If you can convince yourself the expr doesn't do any IO then you can sleep well imo

Ben Sless 2020-04-21T09:09:57.224800Z

In my case I had to create a qualified keyword who's name starts with a number. seems like :1foo is a valid kw but :user/1foo can't be read by the reader. (keyword "user" "1foo") works

Aron 2020-04-21T09:12:47.225Z

this is supposed to run in a browser 🙂

Ben Sless 2020-04-21T09:13:45.225200Z

ah

alexmiller 2020-04-21T12:32:48.227300Z

Spec does not cover Java collections so you’d have to pour one into a Clojure collection first

alexmiller 2020-04-21T12:33:34.228300Z

Spec asserts can be turned off and even compiled out in prod code

Aron 2020-04-21T12:47:24.228500Z

thanks, I realized there has to be some solution because there were blogposts about how to use it, but now I know where to look for it.

eval2020 2020-04-21T16:23:39.230400Z

What spec could validate that a map contains at least one true value?

alexmiller 2020-04-21T16:25:23.230700Z

any function that takes a value can be a spec

❤️ 1
alexmiller 2020-04-21T16:25:46.231200Z

so rephrasing - what function could validate that a map contains at least one true value?

alexmiller 2020-04-21T16:25:55.231400Z

write that function and you're done

eval2020 2020-04-21T16:28:28.231800Z

Ah, I was overthinking this - thanks

Ben Sless 2020-04-21T17:35:32.232Z

This is probably a terrible idea, but I guess everything is possible if you try hard enough

Ben Sless 2020-04-21T17:52:24.232400Z

It would be nice, maybe in spec2, if the meanings of maps and sequences were relaxed a bit. But I'm coming at it from an esoteric use case

alexmiller 2020-04-21T17:52:44.232600Z

that seems way harder than converting the HashMap into a Clojure map

alexmiller 2020-04-21T17:53:05.232800Z

we do not have plans to support Java colls in spec 2

alexmiller 2020-04-21T17:54:33.233Z

(s/valid? ::m (into {} (java.util.HashMap. {::foo 1 ::bar "2"})))

Ben Sless 2020-04-21T17:56:51.233200Z

That's the trivial case, in reality I might be dealing with an arbitrarily nested java collection 😞

Ben Sless 2020-04-21T18:05:26.233400Z

It's also unfortunate because it creates a bit of a mismatch between the validation mechanism and the applied predicates. In this example, get works on java.util.Map, so does seq, so conform* can do its work. The only "hurdle" is map?, which is a pretty stringent requirement. Why not expand spec a bit more towards reflecting intent and less implementation?

alexmiller 2020-04-21T18:15:55.233600Z

the intent is to validate clojure data

alexmiller 2020-04-21T18:16:13.233800Z

validating java colls was out of scope for us