Fork me on GitHub
#clojure-spec
<
2017-05-08
>
caio01:05:27

what about a generator for records? can that be done?

caio01:05:56

https://github.com/clojure/test.check/blob/master/src/main/clojure/clojure/test/check/generators.cljc#L91 one could use this to write the generator for refs, right? (gen/fmap deref (s/gen internal-spec))

caio01:05:12

also, I think my problem is a bit more generic than writing specs for refs. I was trying to write a spec for a type defined in cats. after seeing gen/fmap, looks doable

gfredericks01:05:50

yes technically that will work

gfredericks01:05:28

normal test.check usage is aimed at writing generators for data, not stateful objects

gfredericks01:05:55

I'm not sure what will happen if spec starts having specs for reference types

bhagany01:05:06

@caio I made a generator for reified protocols, which are similar enough to records https://github.com/bhagany/snowth/blob/master/src/snowth/satellites.cljs#L215

bhagany01:05:19

I hadn't learned about gen/let when I wrote this, fyi. I'd probably use it now

caio01:05:11

yeah, I did something like that (though my record was way simpler) https://github.com/funcool/cats/pull/201

caio01:05:40

@gfredericks this is not stateful. IDeref was implemented as a syntatic sugar

gfredericks02:05:01

@caio okay that's not bad then

gfredericks02:05:32

@bhagany btw the bind+return thing there can be simplified to fmap

mpenet07:05:28

any reason why (s/assert x) compiles to x instead of nothing (like core/assert) when s/*compile-asserts* is false?

Niclas08:05:34

Is there any way of specing async channels and the data that may be sent over them?

misha08:05:18

@looveh wrap >! with validating fn? you can put anything on a channel, so any limitation would need to be enforced by client code, I think.

Niclas08:05:03

@misha That’s one way to do it, but it would also be useful to be able to spec channels as function arguments

Niclas08:05:14

Not just the actual get/put on channels

misha08:05:58

wrapping >! would bail right away. wrapping <! would bail sometime later/never. Whichever would suite you best.

misha08:05:03

wrap channel in a record, spec record, spec protocols it implements?

misha08:05:21

*with multispec.

Niclas08:05:49

Yeah that would work, feels a bit hacky though imo since channels are part of a core lib

misha08:05:15

I'd imagine you need to add something to the channel-thing itself to be able to use different specs for different channels.

misha08:05:17

so either spec entry/exit points, or wrap, and spec/pass around/expect wrapped channels.

misha08:05:29

¯\(ツ)

Niclas08:05:18

Hahah that’s amazing!

tbaldridge13:05:00

@misha @looveh specs could be put on a filter transducer on the channel, or a call to map that throws. On an exception the exception would be handed to the channels' exception handler.

misha14:05:00

@tbaldridge I think @looveh's intention was to "spec a function, so that wrong kind of channel passed as an argument would throw", not "make sure channel would not accept random stuff". I like "spec as a filter" much more than macro above, thank you.

misha14:05:28

@tbaldridge can that channel's filter be used as a dispatch value once channel is constructed? is it accessible from outside?

tbaldridge14:05:43

@reefersleep what does explain-data show for the spec code?

reefersleep14:05:55

@tbaldridge: Nothing more.

(spec/explain-data (spec/coll-of ::person []) [{:id 1 :name "john"} {:id 2 :name :heather}])
{:cljs.spec/problems {[] {:pred (coll-checker :sleepydo.db/person), :val [{:id 1, :name "john"} {:id 2, :name :heather}], :via [], :in []}}}

reefersleep14:05:06

Note that I'm talking about cljs, not clj. 🙂

misha14:05:19

@reefersleep why is there [] in (spec/coll-of ::person [])?

misha14:05:28

for vector it is (spec/coll-of ::person :kind vector?), isn't it?

misha14:05:07

clojure gives me

(s/explain-data (s/coll-of string? []) ["a" "b" 1])
CompilerException java.lang.IllegalArgumentException: No value supplied for key: []
and in cljs (js) I can imagine it fails silently somewhere, but produces compromised output anyway

reefersleep15:05:35

I thought I should use :kind vector? as well, until I tried it.

(spec/explain-data (spec/coll-of ::person :kind vector?) [{:id 1 :name "john"} {:id 2 :name :heather}])
----  Could not Analyze  <cljs form>   line:1  column:20  ----

  Wrong number of args (3) passed to: spec/coll-of

  1  (spec/explain-data (spec/coll-of ::person :kind vector?) [{:id 1 :name "john"} {:id 2 :name :heather}])
                        ^--- 

----  Analysis Error  ----
Then I tried without any :kind-indication:
(spec/explain-data (spec/coll-of ::person) [{:id 1 :name "john"} {:id 2 :name :heather}])
----  Could not Analyze  <cljs form>   line:1  column:20  ----

  Wrong number of args (1) passed to: spec/coll-of

  1  (spec/explain-data (spec/coll-of ::person) [{:id 1 :name "john"} {:id 2 :name :heather}])
                        ^--- 

----  Analysis Error  ----
So I looked up the documentation:
(cljs.repl/doc spec/coll-of)
-------------------------
cljs.spec/coll-of
([pred init-coll])
Macro
  Returns a spec for a collection of items satisfying pred. The generator will fill an empty init-coll.

Alex Miller (Clojure team)14:05:56

if you remove that invalid trailing [], clojure says:

user=> (spec/explain (spec/coll-of ::person) [{:id 1 :name "john"} {:id 2 :name :heather}])
In: [1 :name] val: :heather fails spec: :user/name at: [:name] predicate: string?

reefersleep15:05:33

alexmiller: So I guess what I'm seeing is a feature of clj spec missing (as of now) from the cljs port. I kind of suspected this, as I thought I'd seen more useful output in guides/tutorials at the time spec came out.

Alex Miller (Clojure team)14:05:28

which seems better than the schema message to me

andrewboltachev14:05:50

Hello. When I have keys #{:a :b :c} and a spec ::foo, how do I get

{:a (gen/genereate (s/gen ::foo))
 :b (gen/genereate (s/gen ::foo))
 :c (gen/genereate (s/gen ::foo))}
generated for me? (edited) Now with (s/map-of #{:a :b :c} ::foo) I've got only some keys, not all three used

andrewboltachev14:05:24

@misha but I think s/keys takes both value type and key names from it's arguments

andrewboltachev14:05:37

(and I have these different)

andrewboltachev14:05:02

i.e. N keys and single value type

andrewboltachev14:05:15

@misha thanks for answering btw 🙂

misha14:05:51

(s/def :foo/bar integer?)
(s/def :my/a :foo/bar)
(s/def :my/b :foo/bar)
(s/def :my/c :foo/bar)
(s/def :my/map (s/keys :req-un [:my/a :my/b :my/c]))

=> :my/map
(s/exercise :my/map 2)
=>
([{:a -1, :b 0, :c 0} {:a -1, :b 0, :c 0}]
 [{:a -1, :b -1, :c -1} {:a -1, :b -1, :c -1}])

andrewboltachev14:05:34

sure, but I'll need to repeat for 4 or 5 different types of ::foo

misha14:05:07

afaik, you have to alias desired keys to their desired values' specs

misha14:05:55

or provide your own generator, which would make sure all the keys are present. (I'd just alias things, custom generators might screw up s/explain-data error address within the structure)

andrewboltachev15:05:49

yep the custom generators are obviously "approach from the opposite side"

andrewboltachev15:05:14

and well, honestly also having N keys as I do might mean semantically the sequence, not a map

misha15:05:06

plenty of options there as well: s/coll-of, s/tuple

andrewboltachev15:05:33

yep, would take a look at every function

reefersleep15:05:35

I thought I should use :kind vector? as well, until I tried it.

(spec/explain-data (spec/coll-of ::person :kind vector?) [{:id 1 :name "john"} {:id 2 :name :heather}])
----  Could not Analyze  <cljs form>   line:1  column:20  ----

  Wrong number of args (3) passed to: spec/coll-of

  1  (spec/explain-data (spec/coll-of ::person :kind vector?) [{:id 1 :name "john"} {:id 2 :name :heather}])
                        ^--- 

----  Analysis Error  ----
Then I tried without any :kind-indication:
(spec/explain-data (spec/coll-of ::person) [{:id 1 :name "john"} {:id 2 :name :heather}])
----  Could not Analyze  <cljs form>   line:1  column:20  ----

  Wrong number of args (1) passed to: spec/coll-of

  1  (spec/explain-data (spec/coll-of ::person) [{:id 1 :name "john"} {:id 2 :name :heather}])
                        ^--- 

----  Analysis Error  ----
So I looked up the documentation:
(cljs.repl/doc spec/coll-of)
-------------------------
cljs.spec/coll-of
([pred init-coll])
Macro
  Returns a spec for a collection of items satisfying pred. The generator will fill an empty init-coll.

reefersleep15:05:33

alexmiller: So I guess what I'm seeing is a feature of clj spec missing (as of now) from the cljs port. I kind of suspected this, as I thought I'd seen more useful output in guides/tutorials at the time spec came out.

Alex Miller (Clojure team)15:05:18

I think you must be using an older version of ClojureScript - that’s not what I see with the latest

Alex Miller (Clojure team)15:05:44

afaik, ClojureScript is basically at parity with the Clojure version wrt spec

Alex Miller (Clojure team)15:05:46

cljs.user=> (s/explain-data (s/coll-of ::person :kind vector?) '({:id 1 :name "John"}))
{:cljs.spec/problems [{:path [], :pred vector?, :val ({:name "John", :id 1}), :via [], :in []}]}
cljs.user=> (s/conform (s/coll-of ::person :kind vector?) [{:id 1 :name "John"}])
[{:name "John", :id 1}]

Alex Miller (Clojure team)15:05:59

I’m using ClojureScript 1.9.293 btw

reefersleep16:05:43

I thought I was up to date, but apparently, I misread the version number... 1.9.93. 🙂

reefersleep16:05:21

I'll try updating the version, I'm sure that'll make things better for me!

Alex Miller (Clojure team)16:05:36

what’s 200 commits between friends? :)

reefersleep16:05:01

Now I'm having a different problem, not spec-related - I'm trying to destruct with (let [{:keys [a b] :as full} (fn-call something...)] ...

reefersleep16:05:06

I've not changed the code for that destructuring or what goes on underneath between cljs version changes. However, the output of (fn-call ...), which is a map {:a "a's value" :b "b's value"}, - I can tell by printing at the end of my fn-call - turns into {[:a "a's-value] [:b "b's value"} upon destruction (I can tell by printing full), so I cannot destruct`a` or b.

reefersleep16:05:00

(asking in #clojurescript , too)

caio16:05:54

about specing a deftype, this is what I came up with in the end: https://gist.github.com/caioaao/d15260076bb8ee6f48908507ae73155b

caio16:05:03

would be nice if someone could validate if this is safe. I had to go through spec's code for doing this, and some stuff were not really clear to me

caio16:05:33

also saw something in spec-tools that they do some checking on conform that made me think it may not be thread-safe. idk if that's the case

caio17:05:55

oh, nvm. looks like they do this because they call conform* from inside explain*

Alex Miller (Clojure team)17:05:29

What' is either supposed to do?

Alex Miller (Clojure team)17:05:40

Is this just a non conforming or of two choices?

caio17:05:49

it's the either monad, from funcool cats

ikitommi17:05:19

@caio about spec-tools - if the conform doesn’t use threads, I should be ok. But not optimal. would be nice to use just 3rd parameter in conform (http://dev.clojure.org/jira/browse/CLJ-2116)

caio17:05:44

You can say it's a non conforming or between two choices If you want to push it (and probably piss someone 😆 )

ikitommi17:05:53

great article, with the “Safe Dynamic Scope” chapter: https://stuartsierra.com/2013/03/29/perils-of-dynamic-scope.

mobileink18:05:42

I’m getting a strange error that seems related to the switch to spec.alpha. In my code this works: (clojure.core/require [pagespace-sym] :reload). But if I change it to :reload-all I get the following error:

java.lang.ClassCastException: clojure.spec.alpha$regex_spec_impl$reify__1340 cannot be cast to clojure.lang.IFn
clojure.lang.Compiler$CompilerException: java.lang.ClassCastException: clojure.spec.alpha$regex_spec_impl$reify__1340 cannot be cast to clojure.lang.IFn, compiling:(clojure/tools/logging.clj:1:1)

Alex Miller (Clojure team)19:05:30

This is a known issue with spec.alpha not being aot compiled

Alex Miller (Clojure team)19:05:31

It's been fixed in a new build of spec.alpha - 0.1.108. We haven't released a new Clojure alpha yet that depends on it but you can specify that to override