Reputation: 349
(match
[[1 2 3]]
[(:or [_ _ 2]
[3 _ _])] :a0
[(:or [_ _ 1]
[1 _ _])] :a1
:else :else)
=> :else
In this first snippet I expected it to return :a1
.
Weird.
This works:
(match
[[1 2 3]]
[(:or [_ _ 2]
[1 _ _])] :a0
[(:or [_ _ 1]
[3 _ _])] :a1
:else :else)
=> :a0
Is this an expected behaviour?
Upvotes: 4
Views: 185
Reputation: 349
Quoting an answer from reddit:
Seems like a bug in core.match. I used a slightly simpler statement that has the same problem.
(match
[[1]]
[(:or [3] [])] :a0
[(:or [1] [])] :a1
:else :else)
This also returns :else. I ran it through macroexpand and extracted the logic. It turns into something like this.
(let
[x [1]]
(cond (and (vector? x) (== (count x) 1) (= (nth x 0) 3)) :a0
(and (vector? x) (== (count x) 0)) :a0
(and (vector? x) (== (count x) 1) (= (nth x 0) 3)) :a1
(and (vector? x) (== (count x) 0)) :a1))
On the 5th line there you can see the error, it is 3 instead of 1. For some reason it takes the value that is in the first :or pattern instead of the second rows value.
Seems that this patch resolves the problem.
Thanks you all!
P.S.: I haven't tested the patch, though.
Upvotes: 0
Reputation: 180
I believe this is a bug in specialize-or-pattern-row
. I believe the groupable?
test there is wrong because, in your case, it succeeds for your two OrPattern
s, and so the second OrPattern
is replaced with the expansion of the first (the ps
are the subpatterns of the first OrPattern
).
You could work around this by adding a dummy pattern to your second :or
which will force groupable?
to return false:
(match
[[1 2 3]]
[(:or [_ _ 2]
[1 _ _])] :a0
[(:or [_ _ 1]
[3 _ _]
:dummy)] :a1
:else :else)
A possibly better specialize-or-pattern-row
(copy-as
is there to preserve any :as
metadata on the overall OrPattern
by copying the :as
on to each of the subpatterns):
(defn copy-as [dest src]
(if-let [as (-> src meta :as)]
(vary-meta dest assoc :as as)
dest))
(defn specialize-or-pattern-row [row pat ps]
(let [p (first row)]
(if (identical? p pat)
(map (fn [p] (update-pattern row 0 (copy-as p pat))) ps)
[row])))
Upvotes: 2
Reputation: 16045
I'd say it is not because the way you are using :or
is never mentioned in the manual. It is supposed to be used in expressions like that:
[4 (:or 5 6 7) _] :a1
So your code should rather look like
(match
[[1 2 3]]
[[_ _ 2]] :a0
[[3 _ _]] :a0
[[_ _ 1]] :a1
[[1 _ _]] :a1
:else :else)
But just maybe you should consult with the author. It is difficult to say what was the original intention.
Upvotes: 3