Fork me on GitHub
#clojure-spec
<
2022-07-04
>
Martynas Maciulevičius07:07:18

Hey. Is there a good way to spec an atom that has a list of functions? 😄 Is there at least a way to spec an atom?

vlaaad07:07:30

Any predicate can be used as a spec

vlaaad07:07:27

e.g.

(s/def ::fns (s/coll-of ifn?))
(s/def ::my-atom 
  (s/and (s/conformer 
           #(if (instance? clojure.lang.Atom %) 
              @% 
              ::s/invalid))
         ::fns))

(s/valid? ::my-atom (atom [dec identity inc])) ; => true

vlaaad07:07:08

maybe this use of conformer is not idiomatic. tbh I’m not sure what’s an idiomatic use of conformer

vlaaad07:07:09

I think this is not idiomatic, because spec is a “library [that] specifies the structure of data” (as per https://clojure.org/guides/spec), and atoms are state, not data

vlaaad07:07:03

so maybe you could consider not speccing atoms? 🙂

Martynas Maciulevičius07:07:11

I think I'll not do it.

Colin P. Hill13:07:42

Atoms are stateful containers of data. It often makes a lot of sense to spec atoms, especially if they're a highly structured central data store for your application – similar to having a formal schema for a database. The atom function even has an option for including a validator, which could be built on spec (though be mindful of performance and consider making it no-op in prod). https://clojuredocs.org/clojure.core/atom#example-567add80e4b01f598e267e8f

Colin P. Hill13:07:59

A naive (in the sense that it doesn't check function signatures) spec for a list of functions might be (s/* ifn?)

Eric Dvorsak14:07:05

In the core.specs, isn't ::map-special-bindings always going to be a subset of ::map-bindings? how would ::map-special-bindings spec ever be used given that it's only part of :map-binding-form spec which is defined as (s/def ::map-binding-form (s/merge ::map-bindings ::map-special-binding)) https://github.com/clojure/core.specs.alpha/blob/master/src/main/clojure/clojure/core/specs/alpha.clj#L29-44

kwladyka14:07:42

https://clojure.wladyka.eu/posts/form-validation/#trick-about-mirroring-s-def-and-via here is use case showing the difference (s/def ::created ::instant) vs (s/def ::created (s/and ::instant))

kwladyka14:07:20

I don’t know the exact code which you are asking, but I know the difference between this too

Eric Dvorsak14:07:38

I'm not sure I understand your answer, do you mean the intention might just be to produce better error messages?

kwladyka14:07:18

:via [:foo/created], vs :via [:foo/created :foo/instant],

kwladyka14:07:03

I mean without s/and you miss the information about the full path, because it cut corners to make path simpler

kwladyka14:07:23

s/and keep full path and this is ONLY ONE WAY to get this information

Eric Dvorsak14:07:24

ok, although there's no and involved in the code I linked to (clojure.core.spec.alpha), it's a merge of a spec map-bindings with another map-special-binding after double checking it seems to me like the only point of map-special-binding is to keep the map-binding-form spec open to new keywords

kwladyka14:07:01

I don’t know this part of the code, so I can’t really give you deeper answers