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

0 votes
in Spec by

(require '[clojure.spec.alpha :as s]
(s/def :foo/user-map (s/map-of string? int?))
(s/explain-data :foo/user-map {"hi" "foo"})
;; Actual value:
;; #:cljs.spec.alpha{:problems
;;                 ({:path [1],
;;                   :pred int?,
;;                   :val "foo",
;;                   :via [:foo/user-map],
;;                   :in ["hi" 1]})}
;; Expected: the `:in` value to be ["hi"] ?
(s/explain-data :foo/user-map {:hi 2})
;; Actual value:
;; #:cljs.spec.alpha{:problems
;;                 ({:path [0],
;;                   :pred string?,
;;                   :val :hi,
;;                   :via [:foo/user-map],
;;                   :in [:hi 0]})}
;; Expected: I'm not sure, since a path can't "point to" a key

Motivation: given some top-level data (in this case, `{"hi" "foo"}`) and an `:in` path, I would like to be able to find the problematic data (in this case, `"foo"`).

In the case where the value of a map does not conform, the `:in` path is not compatible with functions like `get-in`, but it could be.

In the case where the key of a map does not conform, there is no way to "point to" a key using `get-in`, so I'm not sure what the right fix is.

I don't know that compatibility with `get-in` is a requirement: if spec provided a function that accomplished the same thing with "spec" paths (i.e. ones that could point to keys), that would be fine.

3 Answers

0 votes

Comment made by: jpmonettas

I think that more important than get-in compatible is a way of matching :clojure.spec.alpha/problems inside :clojure.spec.alpha/value independent of the spec that lead to the problem.
For this I think it's important to know if the problem is with the key or the value.

Currently s/map-of reports a path taking into account the map-entry vec, so 0 will be the key and 1 the value.

The problem with what I'm trying to implement is the :in is s/keys which only reports the key.

So when you see a problem in (link: ::k 1) you don't know if it's a problem in the map value or the value is a seq and the problem is in the value at pos 1 in that seq.

0 votes

Comment made by: bbrinck

I agree with what is said above, and I'd like to expand on it after having more experience using the {{in}} path.

AIUI, clojure.spec is not intended to provide easy-to-read error messages. Rather, it is intended to provide a solid foundation so libraries can present errors in a variety of ways.

In my experience in trying to present errors in a different way (Expound), I can report that one of the biggest challenges in trying to interpret the {{in}} path.

There are two cases in particular that are challenging: integer indexes that indicate "key" or "value" failures in a {{map-of}} spec, and integer indexes that indicate positions in "key/value" vectors in {{coll-of}} specs.

I may be just failing to understand the way the 'in' path works, but anecdotally, this is a source of confusion for other library authors. https://groups.google.com/forum/#!topic/clojure/ppnWBJhz-R4 . Additionally, since this path is shown to users when using {{explain}}, a less ambiguous path would help all spec users better understand their errors.

Would it be possible to create unambiguous paths that do not require the reader to know anything about how the path is used? As noted above, the path {{[:key 1]}} can only be disambiguated in by knowing something about the failing data structure. This would likely require replacing the integer indexes with custom records. That would reduce conciseness and readability, but this might be able to be alleviated with new reader macros?

0 votes
Reference: https://clojure.atlassian.net/browse/CLJ-2192 (reported by bbrinck)