Fork me on GitHub

Of coercion: tested the two approaches: conforming-based (spec-tools) and form-parsing based (spec-coerce). Both have big issues. This just can’t be resolved with the current spec architecture. One CLJ-2251 please 🙂


will most likely merge the two approaches into spec-tools, so the st/decode will work without wrapping of specs for many simple forms, fallbacking to conforming-based approach to support all specs (regex included)


Hi! What would be the canonical way to force a map to have a :type key with a specific value with Spec? Would you have:

(s/def ::person (s/and map? #(= (:type  %) ::person) (s/keys ...))


could you use a spec for the :type key like (s/def ::type #{::person})


or is there a better/neater way?


Depends on what you want to do — sometimes you want a multi-spec, where you dispatch on :type.


As a side note, I found that prefer to use boolean ::person? keys rather than a general :type. I'm converting my old code as I go.


@jrychter oh, why the boolean approach?


and under that approach, how would you suggest I handle a hierarchy like this: ?


each of these represent maps of a certain “type” with certain properties


Less complexity, basically. And I don't know, but I suspect that using a single data structure for all of these might not be the best approach.


hey everyone, i may be missing something obvious, but is there a concise way to use spec to perform what is essentially a select-keys according to a spec? e.g.

(def composite-thing { ... })
(s/def ::some-component ...)
(s/select-keys ::some-component composite-thing)
;; => map with keys of composite-thing relevant to ::some-component


or maybe read the relevant keys from a spec’s definition at runtime?


(s/def ::my-map
  (s/keys :req-un [::id]
          :opt-un [::parent-id ::children]))
(->> (s/get-spec ::my-map)
     (s/describe)      ;; get abbrev. form of original spec
     (rest)            ;; drop `keys` symbol
     (apply hash-map)) ;; put kwargs into map
=> {:req-un [:sandbox/id]
    :opt-un [:sandbox/parent-id :sandbox/children]}

wizard 8

I don't think you need (s/get-spec ::my-map) there, you can just do (s/describe ::my-map) (since s/describe calls s/form).

👍 12

remember to walk over special and and ors.

👍 8

This is something I'd really like to see. I know that Rich said that spec is not about limiting what you can do, but there are legitimate use cases where you want to enforce the set of keys and limit it only to what is in your spec (think for example API endpoints).


@jrychter It's easy enough (in most cases) to pull the set of possible keys out of the form for the spec and use that -- treating the spec as the "source of truth" and deriving key sets from it.


I’m building my first really big interconnected system and I have to admit immutablity is driving me crazy!


I just can’t used to passing paths around for my ‘objects’ all the time.


What tare the other functional approaches to this?


I’m really missing OO/imperative systems being happy re: passing around references to objects all the time.


I don't miss that at all 😅


I wish I didn’t!


I’m passing around the paths in to a nested map all the time…


Not sure what you're trying to do, sounds like you have a big nested state, and what about the paths you're passing around?


I’m porting an old AI project to Clojure for now…. the whole system seems thoroughly object orientated. Objects sending messages to others all over the place.


I’m pretty sure Core-async would make things a bit easier since I could just pass the chanell around all the time.


But obviously this is a very different solution.


It sounds like this approach deserves a good refactoring. Core.async would be just swiping the dust under the carpet. Break things down into smaller functions that do one thing only, and are testable.


I was actually asking about your paths, that you're "passing around". I don't know what you mean by that. I can imagine a collection of functions that act upon something flowing through your program. They could be "wired" by a state machine, or a wiring of channels. Why do you think you might need to pass channels around? It's certainly possible, but it might also make sense to push data to a channel that triggers a flow through your functions and something comes out the other end, transformed. That's what I was asking about, with the "what you're trying to do" comment. A description of your data/logic flow in a bit of high-level detail rather than just "porting an old AI project" which doesn't tell much 🙂


ie. What does the old AI project do?