Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by
recategorized by

My application models Piano keys and I'm trying to write specs for them. My initial attempt was simple, they are a map of an octave and a key.

(spec/def ::octave (spec/int-in 0 9))

(spec/def ::key (spec/int-in 0 12))

(spec/def ::note (spec/keys :req-un [::octave ::key]))

The problem now is, that if the octave is zero, only A, A sharp, and B are valid keys. If the octave is 8 only C is a valid key.
This actually matters since converting piano keys to their number on the keyboard would otherwise result in invalid values, like -8 or 96.

My question is, is there a simple way to specify that depending on one value in a map, only certain values are valid for another key? This seems like a common enough problem. Or am I doing something fundamentally wrong?

1 Answer

+1 vote
by
selected by
 
Best answer

I think one answer is to reevaluate whether this is the right data model (maybe numbered keys is the place to start), but leave that aside for the moment.

You can always s/and an arbitrary predicate function that constrains the allowed values.

(defn piano-note? [{:keys [octave key]}]
  ...constraints...)
    
(spec/def ::piano-note (s/and ::note piano-note?))

There isn't a way to do this inside s/keys.

...