Fork me on GitHub
#clojure-spec
<
2018-02-06
>
zalky04:02:30

I'm assuming this is a bug?

Alex Miller (Clojure team)05:02:36

I would expect it to fail in both, surprised it doesn’t in clojure

bmaddy07:02:35

Hmm, what am I missing here?

cljs.user> (require '[cljs.spec.alpha :as s])
nil
cljs.user> (require '[cljs.spec.gen.alpha :as gen])
nil
cljs.user> (gen/generate (s/gen int?))
#object[Error Error: Var clojure.test.check.generators/simple-type-printable does not exist, clojure.test.check.generators never required]
Is there somewhere I should be looking for an example of how to do this in cljs? I couldn't find anything on http://clojurescript.org or in the official spec guide.

bmaddy08:02:52

Oh, and here's part of my :dependencies:

[org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.9.946"]
[org.clojure/test.check "0.9.0"]

bronsa09:02:08

it's not throwing in clojure because asserts are disabled by default

bronsa09:02:46

>>>Can be disabled at either compile time or runtime: If \compile-asserts\ is false at compile time, compiles to x. Defaults to value of 'clojure.spec.compile-asserts' system property, or true if not set. If (check-asserts?) is false at runtime, always returns x. Defaults to value of 'clojure.spec.check-asserts' system property, or false if not set. You can toggle check-asserts? with (check-asserts bool)

zalky15:02:23

@alexmiller: are you sure? In my example, the ::t spec, (s/keys :req [:ns/y])) does not specify anything about the :ns/x key. But it seems to be applying it anyways, simply by virtue of it being defined globally. This would seem to defy the point of :req. Maybe I'm misunderstanding how keys was meant to be used, but I would have thought the clojure version to be correct.

Alex Miller (Clojure team)15:02:05

s/keys is intended to check all registered keys in the map, regardless of whether they are listed in the spec

zalky15:02:32

ah, thanks for clarifying, but then what is the point of :req? Shouldn't you only need :opt?

Alex Miller (Clojure team)15:02:30

req defines the keys that are required (must be present)

Alex Miller (Clojure team)15:02:39

req is about membership

Alex Miller (Clojure team)15:02:04

so it’s really checking a property of the containing map

zalky15:02:16

hmm, but if all keys are just going to be globally applied whether they are in the spec or not, wouldn't that mean all keys a implicit req, unless specified by :opt?

Alex Miller (Clojure team)15:02:20

validating values according to keys is about checking properties of attributes

Alex Miller (Clojure team)15:02:48

no, it does not mean that

Alex Miller (Clojure team)15:02:58

they’re not required, but if they are present, they are checked

zalky15:02:08

Hmm, ok, thanks for the clarification. Is the design assumption that entity attributes should conform the same properties in all contexts? You should never have an entity attribute conform one way in one context, and another way in another context?

zalky18:02:37

@alexmiller: thanks for the responses, the doc was very helpful. My use case was that I was playing with a simple FSM that brought an entity map through one or more workflows. At first spec seemed to lend itself well to defining the transitions in the FSM. For example, an entity with any number of votes is a valid entity, but once it gets a certain number of votes, it transitions to another state in the FSM. Unfortunately having one valid global definition for an attribute seems to place significant restrictions on defining multiple validity states for an entity. Am I missing an easier way to do this with spec, or is it just that spec is not the right tool for this job?

Alex Miller (Clojure team)18:02:01

I find it’s always useful to go back to: what is the truth about this attribute? if I see it in data, what does it mean?

Alex Miller (Clojure team)18:02:36

if it has several possible kinds of values, you can use s/or to specify those alternatives - then you’re stating the truth

Alex Miller (Clojure team)18:02:03

s/multi-spec can be used to choose different combinations of attributes at different points in the fsm

Alex Miller (Clojure team)18:02:23

or perhaps you may want to reconsider whether it’s really the same attribute at every step

Alex Miller (Clojure team)18:02:30

and in that case, maybe the attribute should change

Alex Miller (Clojure team)18:02:45

to be clearer, maybe there is really more than one kind of attribute

Alex Miller (Clojure team)18:02:06

or another option is don’t spec that attribute - you’re expecting it to change over time

Alex Miller (Clojure team)18:02:20

you can still spec functions that operate on that attribute more precisely

zalky19:02:10

Yeah, I was thinking the same thing about the attribute, there are probably times where it indicates that it is really two attributes. However this isn't always the case. The simple example I gave above with the voting is probably a case where splitting would unnecessarily complicating the data model because of an assumptions in the implementation. I think you're suggestion to not spec the attribute via the entity model, and just spec it separately using custom primitives (at least that is how interpreted your last sentence) is probably the one that makes the most sense . Unfortunately you lose some of the great higher order features of the entity model, like being able to define one transition as a merge of previous transitions and some new criteria. One final question: do you think that allowing local bindings in keys would break spec's entity model significantly? Something like:

(s/def ::entity
  (s/keys :req [::attr]
          :let [::attr #(<= 10 (count %))]))

Alex Miller (Clojure team)20:02:28

yes and you don’t need it

Alex Miller (Clojure team)20:02:11

(s/and (s/keys :req [::attr]) #(<= 10 (count (::attr %))

zalky21:02:22

Thanks alex for you time (and your contributions on spec!)