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
borkdude 2020-11-13T16:15:20.356900Z

What is the reason spec chose a custom deftype LazyVar over the built-in delay in its implementation?

alexmiller 2020-11-13T16:17:08.358Z

for which?

borkdude 2020-11-13T16:17:32.358400Z

I'm trying to come up with something that would work in Clojure for this case:

(def input (LazyVar. (fn [] (line-seq (java.io.BufferedReader. (java.io.StringReader. "1\n2\n3\n")))) nil))
such that you don't have to write @input but just input so the first time it's derefed by Clojure it starts its realization. Just a naked line-seq doesn't work, since that starts reading once it's def-ed.

borkdude 2020-11-13T16:17:41.358600Z

@alexmiller in the gen namespace

borkdude 2020-11-13T16:19:06.359Z

I think in Clojure I would have to write a subclass of clojure.lang.Var to make that work.

alexmiller 2020-11-13T16:19:33.359800Z

sorry, I don't actually see what you're talking about

borkdude 2020-11-13T16:20:35.360400Z

And maybe I should just go with @input and not do any magic... This lead to the question: why is gen/LazyVar not just a delay but a custom data structure. Ah right.. sorry, the LazyVar is something in CLJS, not CLJ. 🤦

borkdude 2020-11-13T16:22:38.361100Z

I'll ask over there in #clojurescript

alexmiller 2020-11-13T16:23:09.361400Z

ah

socksy 2020-11-13T18:39:26.363100Z

I am confused about the syntax for (or) and (and) in s/keys. My understanding of it was that anything that is passed to or could be correct, and anything that passed into and has to be present. Did I misunderstand? My intuition does not work for the following example:

(s/def :foo/foo #{:foo})
(s/def :bar/foo #{:bar})
(s/def ::an-int int?)

(s/def ::baz (s/keys :req-un [(or :foo/foo
                                  (and :bar/foo
                                       ::an-int))]))

(s/valid? ::baz {:foo :foo})
;; => false
(s/valid? ::baz {:foo :bar})
;; => true

socksy 2020-11-13T18:51:51.365Z

ok it looks like when you have the same naked keyword (not sure of the right terminology, keyword without the NS) then it will always take the last one defined. That's a huge bummer, since I wanted to be able to spec something like "In this case, do this, in this other case, do this + another piece of data", and the way that was being specced before was by storing everything as a tuple

borkdude 2020-11-13T18:52:38.365700Z

@socksy can't you use s/or for either case?

borkdude 2020-11-13T18:52:52.366200Z

more verbose

seancorfield 2020-11-13T18:53:12.366800Z

@socksy You'll need to wrap s/keys with s/and and add your rules via a predicate -- or use s/or around s/keys as @borkdude suggests.

socksy 2020-11-13T18:53:22.367100Z

yes I think it might be possible. I'll go hit my head on it

socksy 2020-11-13T18:53:36.367600Z

i am thinking my own predicate will probably be the most readable but let's see

borkdude 2020-11-13T18:54:11.368100Z

maybe spec2 has a better answer to this... although I'm not sure if s/select can solve this case

seancorfield 2020-11-13T18:54:45.368400Z

Another option would be a multi-spec I think?

socksy 2020-11-13T18:57:34.368800Z

(s/def ::baz (s/or :foo-case (s/keys :req-un [:foo/foo])
                   :bar-case (s/keys :req-un [:bar/foo ::an-int])))
is not too bad actually

👍 1
borkdude 2020-11-13T18:59:05.369800Z

Now I wonder how you would use this in spec2. I think you would define ::baz as the thing with all possible keys and then use s/select for either case?

borkdude 2020-11-13T19:02:33.371300Z

(s/def ::baz (s/schema [:foo/foo :bar/foo ::an-int]))
(s/def ::foo-case (s/select [:foo/foo]))
(s/def ::boo-case (s/select [:bar/foo ::an-int]))
Something like this?

🤷 1