Search code examples
clojurepattern-matchingclojure-core.match

Is this a clojure.core.match bug or it's just me?


(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?


Solution

  • 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 OrPatterns, 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])))