Spec/merge vs spec/and

Placing this under the Beginners section because I’m completely new to spec.

What’s the difference between s/and & s/merge and when should I choose one over the other?

s/and will check that the map you have is a valid of both the map specs. Like it’ll check if it’s valid to the first spec, and if true, it’ll check if it’s also valid of the other spec. While s/merge will check that the map you have is valid to a union of their required and optional keys.

Since keys spec are open, I think when it comes to validating they’re pretty much the same. But when it comes to using the spec for generating sample maps that satisfy it, s/merge will be able to create a proper generator, while s/and might struggle and require you to provide a custom generator for it. So in general, I’d say use s/merge for maps, so anytime you have a s/and that is only anding map specs, prefer s/merge over it. Use s/and when one of the spec involved is not a map spec.

2 Likes

Another important difference is that with s/and, the conformed value from the first spec flows through the remaining predicates/specs. With s/merge the original value is used for each spec.

3 Likes

Thanks guys,

I’m working through the website material and am currently looking at this section: https://clojure.org/guides/spec#_a_game_of_cards

I wondered how I would use spec to catch cheaters in the model, so I came up with this:

(defn no-cheaters? 
  "Returns true if there are no duplicate cards in the game"
  [{players ::players deck ::deck}]
  (->
    concat
    (reduce deck (map ::hand players))
    count (== 52)))

(s/def ::game
  (s/and
    no-cheaters?
    (s/keys :req [::players ::deck])))

;; test
(s/explain ::game
  {::deck (drop 2 deck)
   ::players [{::name "Kenny Rogers"
               ::score 100
               ::hand (take 2 deck)}]})
;; prints "Success!"

(s/explain ::game
  {::deck (rest deck)
   ::players [{::name "Kenny Rogers"
               ::score 100
               ::hand (take 2 deck)}]})
;; .... - failed: no-cheaters? ...

This works with no-cheaters? as the first or second argument to s/and, but using s/merge instead does not work. Based on what you’ve told me I don’t understand why.

merge is for merging s/keys specs – it can’t be used with a predicate (like no-cheaters?). That’s what @didibus was saying about when to use s/and vs s/merge.

1 Like

Ah, thanks for clearing that up! :slight_smile: