clojure-spec

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?

vlaaad 2022-07-04T07:23:30.826459Z

Any predicate can be used as a spec

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

Ah, I see. Thanks.

vlaaad 2022-07-04T07:28:27.998349Z

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

vlaaad 2022-07-04T07:29:08.323909Z

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

vlaaad 2022-07-04T07:31:09.485059Z

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

vlaaad 2022-07-04T07:32:03.332529Z

so maybe you could consider not speccing atoms? 🙂

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

I think I'll not do it.

Colin P. Hill 2022-07-05T13:26:42.085629Z

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. Hill 2022-07-05T13:28:59.946989Z

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

Eric Dvorsak 2022-07-04T14:18:05.207559Z

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

kwladyka 2022-07-04T14:22:42.813989Z

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

kwladyka 2022-07-04T14:23:20.755509Z

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

Eric Dvorsak 2022-07-04T14:26:38.366489Z

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

kwladyka 2022-07-04T14:27:18.508909Z

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

kwladyka 2022-07-04T14:28:03.749649Z

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

kwladyka 2022-07-04T14:28:23.632619Z

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

Eric Dvorsak 2022-07-04T14:33:24.651109Z

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

kwladyka 2022-07-04T14:36:01.506599Z

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