clojure-spec 2022-07-04

Martynas Maciulevičius 2022-07-04T07:21:18.935059Z

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?

Any predicate can be used as a spec

Martynas Maciulevičius 2022-07-04T07:26:23.023159Z

Ah, I see. Thanks.

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

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

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

so maybe you could consider not speccing atoms? 🙂

Martynas Maciulevičius 2022-07-04T07:33:11.097119Z

I think I'll not do it.

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

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

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

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))

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

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

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

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

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

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

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