Fork me on GitHub
#clojure-spec
<
2017-02-16
>
ag00:02:34

@gfredericks I’m using test.chuck, but can’t get my head around this. How do I generate two dates - one always smaller than the second?

ag00:02:26

I’m using test.chuck.generators/datetime

gfredericks00:02:13

@ag fmap+sort, such-that+not=

ag00:02:13

are you saying I should generate many, sort and then grab first two?

ag00:02:17

makes sense

gfredericks00:02:02

Generate exactly two using tuple or vector

gfredericks00:02:15

Then fmap and filter that

ag00:02:48

filter? what filter for?

gfredericks00:02:25

The filter is to make sure they're not equal

gfredericks00:02:03

("filter" meaning such-that)

ag00:02:46

oh, right… thanks!

Alex Miller (Clojure team)03:02:29

or generate one date and a quantity and add the quantity to the date

viesti07:02:22

For the record, problem that I had yesterday was actually due to using set (#{}) in :req/:opt for s/keys:

user> (s/exercise (s/keys :req #{:lol/id2} :opt #{:lol/id}))
ExceptionInfo Couldn't satisfy such-that predicate after 100 tries.  clojure.core/ex-info (core.clj:4725)
user> (s/exercise (s/keys :req [:lol/id2] :opt [:lol/id]))
([#:lol{:id2 0} #:lol{:id2 0}] [#:lol{:id2 0} #:lol{:id2 0}]…

viesti07:02:44

the s/keys generator also obeys :req and :opt, so generates maps that have only :req keys, which is neat 🙂

viesti07:02:57

another thing, I’m tinkering with generating specs from a source outside code (database schema), and doing something like this:

user> (let [k :lol/id]
        (s/def k int?))
user/k

viesti07:02:39

which is not actually what I wanted, so I’m working around it by doing this:

user> (let [k :lol/id]
        (eval `(s/def ~k int?)))
:lol/id

viesti07:02:13

Which seems a bit odd. Am I missing something or is the idea that specs are defined firstly in Clojure code and not derived from outside sources?

Oliver George11:02:23

I faced similar problem when I wanted to generate specs based on an sql db schema by inspection. In that case I generated clojure code.

Oliver George11:02:49

The macro based API complicates things.

Oliver George11:02:03

It must be possible to do. I'd love to see examples.

Alex Miller (Clojure team)15:02:26

you can do that, or use eval

Alex Miller (Clojure team)15:02:51

in the future there may be a lower-level function-based api, however it will not be able to automatically capture forms for explain reporting

mpenet15:02:47

that'd make the life of all the spec to <whatever-format> projects a lot easier

ikitommi16:02:47

thumbed that one up, thanks @mpenet. Hacked around that just yesterday to get dynamic conformers “drop extra keys” & “fail on extra keys” working for map specs.

Alex Miller (Clojure team)16:02:26

@mpenet has been in the plan from the beginning, but has been blocked behind the various form errors

eraserhd17:02:23

It would be cool to use s/exercise to generate a sample structure for documentation, but I would need to tweak a few things: Make all keys required, ensure all collections have >= 1 element.

eraserhd17:02:40

I realize this would probably be hacky, but any thoughts on how to do this?

gfredericks19:02:30

I'd probably write a function doc-sample that has the special cases you described, calls itself recursively, and falls back on gen/generate when it doesn't know what to do

calvis23:02:58

I’m writing a macro that is essentially a wrapper around defn, so I thought it would be cute to use defn‘s spec to conform for easy access to the args & body. it works on some, but not when the body of the macro is a map, because..

user> (s/conform :clojure.core.specs/defn-args
                 '(foo [bar] {:baz 42}))
{:name foo, :bs [:arity-1 {:args {:args [[:sym bar]]}, :prepost {:baz 42}}]}
the body conforms as :prepost rather than :body. is there any way to make it backtrack and choose the path where :prepost is empty and :body is correct?

gfredericks23:02:20

oh man; does that mean defn is essentially unspecable?

gfredericks23:02:38

I mean I guess you could rewrite it with an or

calvis23:02:37

well I don’t think it’s un-specable, just that the conform result does not align with the semantics

gfredericks23:02:14

that's presumably because the spec is ambiguous

gfredericks23:02:31

and I don't think it should be

calvis23:02:05

well yeah, I think the spec as written is a bug / oversight

bronsa23:02:17

i'd open a ticket about that

calvis23:02:26

just hoping for a way around it

gfredericks23:02:33

change the spec yourself 🙂

calvis23:02:57

more work than I was hoping to do

gfredericks23:02:00

you'd wrap the :prepost and :body in an or or alt or whatever, where the body uses + instead * in the branch with the prepost

calvis23:02:08

yeah I agree

bronsa23:02:27

(s/def ::args+body
  (s/cat :args ::arg-list
         :rest (s/alt :body+attr (s/cat :prepost (s/? map?)
                                        :body (s/+ any?))
                      :body (s/* any?))))

bronsa23:02:29

this works

bronsa23:02:41

user=> (s/conform :clojure.core.specs/defn-args '(foo [bar] {:baz 42}))
{:name foo, :bs [:arity-1 {:args {:args [[:sym bar]]}, :rest [:body+attr {:body [{:baz 42}]}]}]}
user=> (s/conform :clojure.core.specs/defn-args '(foo [bar] {:baz 42} 1))
{:name foo, :bs [:arity-1 {:args {:args [[:sym bar]]}, :rest [:body+attr {:prepost {:baz 42}, :body [1]}]}]}

gfredericks23:02:55

@bronsa no need for the s/? at that point, right?

bronsa23:02:21

what about (foo [bar])

gfredericks23:02:59

that would go through the second branch

bronsa23:02:20

ah sorry, I read s/? and thought s/*

bronsa23:02:37

yeah no need for that s/? indeed

bronsa23:02:46

better name for :rest?

bronsa23:02:08

:body-or-body+prepost doesn't look too good :)

gfredericks23:02:32

:body+ :body* :body' dunno too many names

calvis23:02:32

seems like the spec should be named ::args+prepost+body and the :rest is ::prepost+body

calvis23:02:08

further broken down into :prepost+body or :body

bronsa23:02:04

:?prepost+body -> :prepost+body/:body

bronsa23:02:23

I guess an argument could be made that prepost is part of the body

calvis23:02:49

given the original name of the spec that seems to be the direction they went

bronsa23:02:43

aight, I'll make a ticket + patch and just keep it as

(s/def ::args+body
  (s/cat :args ::arg-list
         :body (s/alt :prepost+body (s/cat :prepost map?
                                          :body (s/+ any?))
                     :body (s/* any?))))
and rich/alex can figure out some better naming

bronsa23:02:20

hmm, maybe prepost should be stricter than just map?

bronsa23:02:35

the actual grammar for defn only accepts a map with :pre [fn*] :post fn

calvis23:02:30

still an improvement ¯\(ツ)

bronsa23:02:06

it ignores any other key

gfredericks23:02:26

it looks to me like the only requirement is :pre's value is a collection

bronsa23:02:45

an throw at runtime if you don't provide [fn*] for :pre or fn for :post

gfredericks23:02:31

I thought they were arbitrary expressions

bronsa23:02:41

arbitray predicates

bronsa23:02:47

i'm using fn to mean IFn instance

bronsa23:02:34

no you're right I'm stupid

gfredericks23:02:42

have you been using pre-post wrong for years and didn't notice because the functions are always truthy 😄

bronsa23:02:06

yes, absolutely

gfredericks23:02:34

dynamic languages are a magic surprise

bronsa23:02:08

@gfredericks the few slack users that use the slack->irc bridge will know what we've been up to

bronsa23:02:14

we need to find them and silence them

gfredericks23:02:46

glad I stopped using that when the threads feature was released

bronsa23:02:58

oh god I don't want to know how that looked

gfredericks23:02:57

I think it just looked like regular messages

gfredericks23:02:18

so I would've ended up unknowingly replying to thread messages in the main channel

gfredericks23:02:00

I can put up with all sorts of nonsense for the sake of technological idealism, but unknowingly looking like a crazy person is not one of them

bronsa23:02:37

one thing I really miss about the #clojure irc: slack makes me feel so much more guilty when I derail a conversation/channel into offtopic banter

gfredericks23:02:01

only because threads exist? or because topics are more specific?

bronsa23:02:08

the latter I think