Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

s/or ambiguity with "un-ordered" or clauses #31

Open
joelsvictor opened this issue Oct 7, 2024 · 2 comments
Open

s/or ambiguity with "un-ordered" or clauses #31

joelsvictor opened this issue Oct 7, 2024 · 2 comments
Labels
enhancement New feature or request priority:low

Comments

@joelsvictor
Copy link

Hello,

I am having some trouble coercing a heterogenous collection using coax.
If you have a look at the example below, the ip collection has three different types of values. I want to convert them to the vector defined in op. The transformation is simple, just convert every string to a keyword.

I have written two specs for illustration.
Here op conforms to spec-op. spec-ip is same as spec-op with keyword? replaced by string?.
It can be seen that ip conforms to spec-ip as expected. This illustrates that the spec is correct.

The problem arises when I try to coerce ip using spec-op. It is failing when trying to coerce the third entry in the vector i.e. ["f3" ["a1", "a2", "a3"]]. The conform accepts the input.
This seems to be a bug in coax because conform is working as expected but coerce is not working.

  (def ip ["f1"
           ["f2" "a1"]
           ["f3" ["a1" "a2" "a3"]]])

  (def op [:f1
           [:f2 :a1]
           [:f3 [:a1 :a2 :a3]]])

  (s/def ::fn-with-var-arg-kw (s/tuple keyword? (s/coll-of keyword?)))
  (s/def ::spec-op
    (s/coll-of (s/or :fn keyword?
                     :fn-with-arg (s/coll-of keyword?)
                     :fn-with-var-arg ::fn-with-var-arg-kw)))

  (s/def ::fn-with-var-arg-str (s/tuple string? (s/coll-of string?)))
  (s/def ::spec-ip
    (s/coll-of (s/or :fn string?
                     :fn-with-arg (s/coll-of string?)
                     :fn-with-var-arg ::fn-with-var-arg-str)))

  (s/conform ::spec-ip ip)
  ;; => [[:fn "f1"]
  ;;     [:fn-with-arg ["f2" "a1"]]
  ;;     [:fn-with-var-arg ["f3" ["a1" "a2" "a3"]]]]

  (s/conform ::spec-op op)
  ;; => [[:fn :f1]
  ;;     [:fn-with-arg [:f2 :a1]]
  ;;     [:fn-with-var-arg [:f3 [:a1 :a2 :a3]]]]

  (c/coerce ::spec-op ip)
  ;; => [:f1 [:f2 :a1] [:f3 ["a1" "a2" "a3"]]]
@mpenet
Copy link
Member

mpenet commented Oct 11, 2024

Hi,

I kind of missed the notification about this one, sorry about that.

It's an ambiguity issue. I will have a look next week and fix it, but basically:

the or with :
:fn-with-arg (s/coll-of keyword?)
:fn-with-var-arg (s/tuple keyword? (s/coll-of keyword?))

coax tries the or specs in order, the first one modifying any value in the data is considered to be the one to be picked up, so it never reaches fn-with-var-arg. So coax decides to pick up fn-with-arg, modifies what it can to match keyword? and leaves the rest alone (as it does for anything else).

@mpenet
Copy link
Member

mpenet commented Oct 11, 2024

Until then you can workaround it by changing the order in the s/or and registering a custom coercer for fn-with-arg that will reject the value when it should:

(s/def ::fn-with-var-arg-kw (s/tuple keyword? (s/coll-of keyword?)))
(s/def ::fn-with-arg (s/coll-of keyword?))
(s/def ::spec-op
  (s/coll-of (s/or :fn keyword?
                   :fn-with-arg ::fn-with-arg
                   :fn-with-var-arg ::fn-with-var-arg-kw)))
[...]

(c/coerce ::spec-op ip
          {:idents {::fn-with-arg
                    (fn [x _opts]
                      ;; could also be a call to a spec
                      (if (and (coll? x)
                               (every? #(or (string? %)
                                            (keyword? %))
                                       x))
                        ;; it matches we know for sure it's the a fn-with-arg, no more ambiguity, coercing is safe.
                        (c/coerce ::fn-with-arg x)
                        ;; otherwise it's invalid, the next `or` clause will be tried
                        :exoscale.coax/invalid))}})

Not great but better than nothing until it gets fixed.

@mpenet mpenet changed the title Coercion not working for tuple spec s/or ambiguity with "un-ordered" or clauses Dec 17, 2024
@mpenet mpenet added priority:low enhancement New feature or request labels Dec 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request priority:low
Projects
None yet
Development

No branches or pull requests

2 participants