This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-01-14
Channels
- # announcements (6)
- # architecture (5)
- # aws (4)
- # beginners (79)
- # boot (3)
- # boot-dev (7)
- # calva (21)
- # cider (17)
- # cljdoc (12)
- # clojure (83)
- # clojure-art (2)
- # clojure-belgium (2)
- # clojure-brasil (1)
- # clojure-estonia (2)
- # clojure-europe (3)
- # clojure-finland (5)
- # clojure-india (2)
- # clojure-italy (49)
- # clojure-losangeles (1)
- # clojure-nl (12)
- # clojure-spec (120)
- # clojure-sweden (2)
- # clojure-switzerland (4)
- # clojure-uk (31)
- # clojurescript (80)
- # data-science (17)
- # datavis (2)
- # datomic (31)
- # emacs (31)
- # figwheel-main (28)
- # fulcro (6)
- # jobs (2)
- # liberator (7)
- # luminus (1)
- # nrepl (2)
- # off-topic (51)
- # overtone (2)
- # pathom (4)
- # re-frame (28)
- # reitit (1)
- # rum (6)
- # shadow-cljs (26)
- # specter (2)
- # tools-deps (33)
- # yada (3)
i wonder why aren't specs first class things we can define literally and pass around, and why they need their own special registry when we already have namespaces and vars? :thinking_face:
i'm playing with building up specs in an automated way (e.g. reducing a data structure into a spec representation). looks like i'm gonna have to s/def
things along the way. isn't that some kind of PLOP? maybe this is something that will be improved in future version đ
Why another registry, I believe to not clutter vars/nses more and make their potential evolution separate?
then why not have a registry for all atom
s? and another for all fn
s? and on and on. đ
There are already issues with vars initialization at startup right now, I guess that's not to make it worse among other thing. Then it's a "private" thing, we never get exposed to the fact it's separate
We ll see with alpha2 I guess. The posts from @alexmiller about the ongoing work on this are quite interesting
they don't reveal much about these 2 questions in particular, but soon enough it might
@devth if you can give an example, we can see if it can be improved using the current version of spec?
tried to explain the problem in detail at https://clojurians.slack.com/archives/C1B1BB2Q3/p1547482812312100
@devth there is s/spec
which gives you spec w/o being registered, which might be useful for you if you build specs dynamically, use, and throw away right away.
all of the programmatic construction aspects will be different in spec 2
@alexmiller could you elaborate more on that? Will existing dynamic specs break?
hard to say yet
Does there exist a flavor of s/merge
for disjunctions, i.e. s/or
? I have a base set of disjunctions (A or B) that Iâd like to be able to extend in certain contexts (base or C or D).
@devth Isnât this the flaw of current spec that Rich spoke about in his recent Clojure Conj talk?
still curious about the place vs value oriented nature of spec :thinking_face: any thoughts?
Big ideas: 1. separating out schema (shape) and selection (optionality). cool stuff! - it's still not "just data". why the departure? everything in Clojure is just data, and for good reason. the entire core lib is built around manipulating data. it's the best part of clj. - it's still place oriented, at least from what i gather so far đ 2. Better programmatic manipulation of specs. great! but again: why not make it data? then programmatic manipulation comes with. Imagine if the schema for:
{:weather {:weatherbitio {:key "xoxb" :default {:zip "98104"}}}
:command {:prefix "!"}}
Was simply:
{:weather {:weatherbitio {:key string? :default {:zip string?}}}
:command {:prefix string?}}
There's elegance in mirroring the data structure you're specifying, kinda like how Datomic Pull queries mirror the data you get get back.
:thinking_face:or do you mean âwhy do I have to gives names to each sub-spec, canât you inline them?â ?
future spec will allow you (I think!) to spec the whole schema and then define selections on them, so you donât have to name each selection.
Iâm working on it every day
hard to say when the next point will be where itâs useful to look at but it wonât be âaâ next version - itâs going to be a series of releases
so to be clear: currently there isn't necessarily a better way to achieve what i'm doing? i have to dynamically s/def
a bunch of stuff?
@devth there is a lib called spec-tools which may help you accomplish this, but I expect breaking changes when new spec comes out
@devth https://github.com/metosin/spec-tools#transforming-nested-specs applies to your situation I think
@devth o wait, maybe it was this: https://github.com/metosin/spec-tools#data-specs (the README is so long ;))
most of things spec-tools is built on are going away, but there will be one or maybe even two alternative paths to this goal
not familiar enough with spec-tools to have an opinion on whether it should have been multiple libs. but long searchable READMEs with lots of examples are always nice.
i think i'll play with spec-tools for now. if things change i can always port to the new way
(create-ns 'my.really.long.ns)
(alias 'mrln 'my.really.long.ns)
::mrln/attribute
Is this trick for shorter namespaced keywords likely to get me scolded by experienced clojure spec devs? Is it better to formally create the ns file even if itâs largely empty?https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/test/alpha.clj#L19
@cjsauer I tried moving this to a proper ns, so CLJS could use the alias as a namespaced keyword as well, but that was rejected: https://dev.clojure.org/jira/browse/CLJ-2421
Ah interesting, thank you. in-ns
is a bit better. Which part of this does cljs not like? I am hoping to use these specs on both server and client.
(ns dude)
(in-ns 'foo)
(defn foo [x])
(in-ns 'dude)
(defn dude [x])
$ clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.439"} org.clojure/test.check {:mvn/version "RELEASE"}}}' -m cljs.main -t node -c dude
WARNING: dude is a single segment namespace at line 1 /private/tmp/repro/src/dude.cljs
WARNING: Use of undeclared Var dude/in-ns at line 3 /private/tmp/repro/src/dude.cljs
WARNING: Use of undeclared Var dude/in-ns at line 7 /private/tmp/repro/src/dude.cljs
ShootâŚso then portable code will have to use the qualified keyword everywhere at the moment?
did that in CLJS: https://github.com/clojure/clojurescript/commit/731be5e0916ad8d619c302bfc9b985c4d10daa8d
I may actually fall back to formally creating the namespace file for now, even if its mostly empty. Keyword fatigue is realâŚ
itâs not a huge pain. you could make a function that creates the keyword if you want to save characters
@cjsauer we also have that in the app Iâm working one, several almost empty ns-es for thisâŚ
What is the philosophy behind specâing attributes that are intended to be used as references to other entities? For example, I might have ::company/employees
which models a one-to-many relationship. How would I spec this attribute? My intuition is to use something like (s/coll-of ::employee)
, but the issue here is that I now have to define some minimum key-set that is ::employee
, which is a spec that I think is largely contextual. Further, there may be contexts where (s/coll-of (s/tuple #{::employee/uuid} ::employee/uuid))
are valid ::company/employees
, e.g. datomic idents.
Ultimately what I think Iâm wondering is: can reference attributes only be specâd within a given context, or at specific boundaries (e.g. fdef
)?
I think my confusion stems from the fact that a :db.valueType/ref
attribute can take on lots of different forms throughout an application, and so itâs difficult to spec. For example, it could be a raw entity id, a {:db/id 000}
map, a [:db/id 000]
ident, some kind of custom [:employee/uuid #uuid "000"]
ident, or it could be a fully formed employee mapâŚall these can values live under the same ref attribute at one time or another.
>I think only spec-ing the pull form (ie what you would get from a pull) Iâve been playing with a similar strategy. This way you can still make good use of generators without the concept of a database getting involved.
>I think the TX spec would only spec the coll not the keys individually
@favila by this do you mean something like (s/coll-of (s/keys))
?
Coll of either Vecs with entity ref (= 2-tuple, long, or keyword) first item (Tx fns and raw add/retract) or tx maps; which are maps with entity ref keys and Tx map or col-of-txmap or entity ref or non-ref value or coll of non-ref value)
In my tx function fdefs Iâve been specifying the :ret
much more tightly. Iâm less interested in specâing the :ret
of any tx function, and more interested in specâing this specific tx function.
Iâve been staring at the code more, and I think whatâs troubling is that when I want to specify the form of some ref attribute in general, it canât really be doneâŚthe question of when is inescapable so to speak. Because if I specify the pull form for all of my ref attributes, now Iâve bound the keyword :company/employees
to one specific context: the fully realized one. I canât use the name :company/employees
in other contexts in which it might make sense, because spec has already registered the âone true formâ. Does this make sense, or am I complicating the issue? :thinking_face:
For example, a tx function might return a collection that includes a map with the :company/employees
keyword in it, but it may not conform to the pull form. It may be a collection of idents instead, meaning it references pre-existing entities.
Because Iâve already specâd it to conform to the pull form, I canât use that keyword again in a new contextâŚ
I had the same idea, and it works okay. I think the downside is that it forces you to resort to custom generators pretty much everywhereâŚ
>But this is the exact problem Rich stated in his last conj keynote Maybe Not I think so as well. Pretty much anywhere that a map has to be specified, this problem will arrive. The concept of âentityâ is still difficult to grasp in my opinion. âEntityâ doesnât seem to become concrete until some data arrives at a boundary/function.
spec has a strong opinion that the keyword of a map is (essentially) describing the type of its contents
but there are cases where that is not true and the keyword is really a structure rather than a type tag
and foreign system's data routinely make their map entries contextual to the type of thing they are in
both these use cases I have found are bad fits for spec; the only workaround is predicates
what I mean is they are like structurally-expressed arguments to some function call implied by the dsl
{:my/attr [:db/id :db/ident]}
is not expressing a :my/attr
value, it's telling you to do something to a :my/attr
value in some different context
so, maybe multi-spec on the key as the dispatch value is getting towards the right answer?
>I donât think this is the same problem Rich has acknowledged @favila Iâve just rewatched Maybe Not, and I think youâre right, itâs not the same issue. It is exactly the type/structural conflation that youâve identified.
if anything, rich is moving even further away from keywords having any contextual meaning
He actually also mentions that :ret
in function specs is starting to smell, and itâs in those :ret
specs that I encountered this issue. So that may be tellingâŚ
If I ease up on trying to ânail everything downâ with specs, as Rich puts it, it may alleviate some of these limitations
He touches on it at around this point in the video: https://youtu.be/YR5WdGrpoug?t=3174
what heâs saying there is that ret specs donât tell you much, you almost always want fn specs. so he wants to refine fn specs (make them easier to use?)
TrueâŚI think the root of it is definitely this âcontextual keysâ idea (keywords have different specs in different contexts). What might be cool is the ability to define a âdisjunctive schemaâ:
(s/def ::widget (s/or* :A string? :B pos-int?))
and then use a function similar to Richâs proposed s/select
function in order to mask the contextually relevant predicates, e.g. (s/select* ::widget [:A])
would mean âin this context, only condition :A
can match (anything other than a string would result in error)â.