beginners 2025-06-25

how do experienced clojure dev validate a multi-arity constructor fn? Do you prefer the below? or do you spec them? or do you use assertions? or something else:

(defn octet-string
  ([]
   (->OctetString (byte-array 0)))
  ([^bytes raw-value]
   (when-not (bytes? raw-value)
     (throw (IllegalArgumentException. "Expected byte array")))
   (->OctetString (java.util.Arrays/copyOf raw-value (alength raw-value))))
  ([^bytes raw-value ^long offset ^long length]
   (when-not (bytes? raw-value)
     (throw (IllegalArgumentException. "Expected byte array")))
   (when-not (and (int? offset) (>= offset 0))
     (throw (IllegalArgumentException. "Offset must be a non-negative integer")))
   (when-not (and (int? length) (>= length 0))
     (throw (IllegalArgumentException. "Length must be a non-negative integer")))
   (when (> (+ offset length) (alength raw-value))
     (throw (IllegalArgumentException. "Offset + length exceeds array bounds")))
   (->OctetString (java.util.Arrays/copyOfRange raw-value offset (+ offset length)))))
(i use records here because i want to use protocols and performance matters)

In this case, I wouldn't validate anything since if something is wrong Arrays/copyOf itself or Clojure reflector would throw. Sometimes I use :pre or assert when a clear message is useful, but only if the error is mostly for me or other developers working on the same project. Specs/schemas - only when the error is for someone else, like e.g. when a public API is called. At that point, the schema is also made a part of the API description.

🙏 1