Fork me on GitHub
#clojure-spec
<
2017-10-20
>
eriktjacobsen00:10:47

@alexmiller Thanks for the quick answers. Explained: https://redd.it/77if23 If the answer is "you need a separate namespace per data type with colliding keywords", so be it.... just wanted to have the discussion.

seancorfield00:10:36

@eriktjacobsen Yes, the answer is to provide unique namespace-qualifiers for each of the colliding keywords -- remember that those namespaces do not have to exist, but they should have names that represent the different concepts being modeled.

seancorfield00:10:22

The whole point of namespaces is to provide unique ways to name things that are distinct (but otherwise have the same unqualified name).

eriktjacobsen01:10:53

Right, it's just unfortunate to lose the relationship of between using the spec and knowing where it's defined, losing ability to use the :: and aliases (until patched), and other limitations mentioned. I understand that might be the answer, its just my team is unhappy if that's the final answer.

gfredericks01:10:35

:foo.bar/baz.hullabaloo is a valid keyword I think

gfredericks01:10:56

I don't suppose it's likely to make anybody happy though

eriktjacobsen01:10:42

Correct, but when used in a (spec.keys :req-un) it will require the keyword to be :baz.hullabaloo, correct? Rather than :hullabaloo. That is basically the same solution as my first proposal, just with . instead of /

gfredericks01:10:15

yep. it's just sadness all over

eriktjacobsen01:10:08

it does seem like differentiation between specspace and namespace seem slightly confused, or dare I say complected.

gfredericks01:10:02

OTOH it would be weird and confusing to have many parallel namespace systems e.g. those lisps that keep functions separate from the other kind of thing

potetm01:10:24

Why use :req-un for a namespaced key?

eriktjacobsen01:10:53

Because the incoming data I'm trying to spec is generally using unqualified keys. Such as from parsing json.... I suppose I could add a step before doing spec validation that transforms all the unqualified keys into qualified keys using some mapping scheme, validates with spec, then unqualifies them again for usage downstream, but that's basically the functionality i'm looking for from spec and seems like a lot of overhead

potetm01:10:13

Ah, okay. It sounded like you had control over the keyword. That makes sense though.

jrychter07:10:28

@eriktjacobsen FWIW, I am struggling with similar issues. I tried (briefly) using spec keywords in "fictional" namespaces (e.g. not corresponding to clojure ns), but then I end up with an explosion of long namespaced keywords. Lots of typing, lots of things to read and process when looking at code. The :: really helps in reducing that.

jrychter07:10:57

Somewhat related: I'm also thinking about dropping :req-un and moving to fully-qualified keys everywhere. But there is a price to be paid in storage (JSON database), transmission (websocket trafic isn't compressed, Transit helps but I'm not sure to what extent), code complexity (need to include keyword namespaces in all destructuring code), and ClojureScript code building keywords from strings. I'm not sure what that price is.

mpenet08:10:48

if you throw in some utils it's not so bad

mpenet08:10:02

i use a macro that creates "relative" aliased ns and tend to spec maps using it following a pattern like ::map ::map/foo ::map/bar ::map/baz etc

mpenet08:10:46

(defmacro rel-ns [k] `(alias k (create-ns (symbol (str ns "." (str k))))))

mpenet08:10:15

you can just do (rel-ns 'foo.bar.baz.bad) if deeply nested and then use it as ::foo.bar.baz.bad/xxx, it's quite readable imo

eriktjacobsen18:10:18

Thank you! We have thought of similar workarounds, mostly wanted to bring the discussion up for a solution from the core.specs team, though we could incorporate this if we have to workaround it.

metametadata16:10:39

Hi! Given ::allowed-val "enum spec", is there a way to print all the allowed values in the error message? Currently it prints (into #{} allowed-vals). And let's consider it's the requirement that I need allowed-vals extracted as a var because I need to iterate over it in other places.

(def allowed-vals [1 2 3])
(s/def ::allowed-val (into #{} allowed-vals))
(s/explain ::allowed-val 5)

=>
val: 5 fails spec: :cljs.user/allowed-val predicate: (into #{} allowed-vals)
:cljs.spec.alpha/spec :cljs.user/allowed-val
:cljs.spec.alpha/value 5

hiredman17:10:25

user=> (s/def ::foo #{:a :b :c})
:user/foo
user=> (s/form ::foo)
#{:c :b :a}
user=> (doseq [i (s/form ::foo)] (prn i))
:c
:b
:a
nil
user=> 

metametadata17:10:59

yeah, it's a bit backwards but should work, thanks

ajs20:10:21

There is no forwards or backwards with a set, right? Unordered.

metametadata00:10:35

right 🙂 I meant that it's backwards in a sense that I'd like the variable to be the source of allowed values instead of putting them into spec

metametadata18:10:17

and what if the enum values cannot be hardcoded like that in spec? e.g. when I need to read them from file

ghadi18:10:10

right now calling eval to build specs is probably the best choice, but work is apparently underway on making spec more "programmable" -- make specs from external stuff

bsima20:10:35

how would I spec a function with variadic keyword args like (myfn :a 1 :b 2)? I’m trying to use (spec/* (spec/keys :req-un [::a ::b])) but that’s not right

bsima20:10:27

ah thanks

falak22:10:15

Can somebody help me out with writing a fdef for a multi-method using defmulti?

taylor22:10:53

I might be able to in a little while. Do you have an example?

falak22:10:39

I have a function which takes 2 maps as input and returns a vector with 2 maps.

defmulti my-function (fn [map1 map2] (:type map2)

defmethod my-function :type1
  [map1 map2]
  (let .... 
        ...
        ...
        ...)
[ret-map1 ret-map2]

defmethod my-function :type1
[map1 map2]
.
.
.

falak22:10:40

This is what I tried for fdef -

(s/fdef my-function
             :args (s/cat :map1 map? :map2 map?)
             :ret    vector?)

taylor23:10:35

Not sure exactly what’s not working but this works for me:

(defmulti my-function (fn [map1 map2] (:type map2)))
(defmethod my-function :type1 [map1 map2]
  [map1 map2])
(defmethod my-function :type2 [map1 map2]
  [map2 map1])
(s/fdef my-function
        :args (s/cat :map1 map? :map2 map?)
        :ret vector?)
(stest/instrument `my-function)

falak00:10:55

So how do I test the dispatch-value of the dispatch-fn in the defmulti definition?