Fork me on GitHub
#clojure-spec
<
2020-01-07
>
telekid18:01:28

I’ve got a spec(ish) design question that I’ve been thinking about for a while. I finally decided to throw it in a gist: https://gist.github.com/telekid/0f276bfa3b3f0d395a7a71158adbb35d I’m curious to know how other people approach this problem?

vemv20:01:49

In a given (ns models.geography.continent), I tend to solve this with:

(spec/def :models.geography.continent.penguins/animals ...)

(spec/def :models.geography.continent.lions/animals ...)
i.e. I don't use :: syntax for these cases, and I don't create additional Clojure namespaces (files). I call it the "synthetic ns pattern" It's a bit verbose, and losing the 1:1 mapping between cljs namespaces and spec namespaces isn't ideal either. But it's fairly occasional so I've been happy to use these for a couple years

vemv20:01:55

btw, not sure if this solution is already described in your gist. sometimes I'm impatient 🙃

telekid20:01:57

yeah, I like that

telekid20:01:26

I’ve experimented with that a bit in a few places (but I forgot about it when I was writing the gist - thanks for bringing it up)

vemv20:01:32

✌️! I realised, a more accurate nickname would be "synthetic sub-ns pattern". so, while it's not a 1:1 mapping, one is still reasonably close to that ideal

Eddie18:01:19

@jake142 If ::animals/penguins is defined as a count (perhaps s/def ::penguins nat-int?) then I do not see a problem with reusing it. EDIT: Gist has been updated. Disregard! :)

telekid18:01:17

Oh, I just realized that I made a typo

telekid18:01:24

one moment

telekid18:01:52

L20 how has ::animals/lions

Eddie18:01:37

I see. Thanks for the clarification.

telekid19:01:23

Sorry for the confusion!

kenny21:01:10

Why were multi-specs implemented using multi-methods? I have never found the dynamism of multimethods useful in defining multi-specs. In fact, I think I'd prefer all of the multi-spec types to be declared statically.

ghadi21:01:17

@kenny so that they're open to extension

ghadi21:01:56

in fact I can't think of anything in clojure that's closed

kenny21:01:11

Right. At least interally, I've never found that useful. All type->keys are declared upfront. Multimethods make it hard to determine the input data.

ghadi21:01:55

> All type->keys are declared upfront. ?

kenny21:01:14

Dispatch functions

ghadi21:01:44

I'm sorry I still don't understand

kenny21:01:26

For a particular entity, I know it has types A, B, C. It can never and should never be extended except directly in the definition.

kenny21:01:52

All the variants of an entity are known upfront.

ghadi21:01:09

ok that's your use case

Joe Lane21:01:12

You might not be the one defining the multispecs, the users of your library may be.

💯 4
ghadi21:01:47

and all the extensions in the extensions folder

kenny21:01:37

Oh right, I see the use for libs. This internal use case is much different. Perhaps some sort of new macro is what we need:

(multi-spec2 {:type1 (s/keys :opt [])
              :type2 (s/keys :opt [])})

kenny22:01:49

For those interested...

(defmacro closed-multi-spec
  [name dispatch-key dispatch->spec]
  (let [defmethods (map (fn [[dispatch spec-form]]
                          `(defmethod ~name ~dispatch
                             [~'_]
                             ~spec-form))
                        dispatch->spec)]
    `(do
       (defmulti ~name ~dispatch-key)
       ~@defmethods
       (s/multi-spec ~name ~dispatch-key))))
Don't know if this is where it'll land but it's a start.

ghadi21:01:22

wouldn't it be amazing if you can write that macro today?

😉 4
philosoraptor 4