Fork me on GitHub
Ethan Miller01:03:28

Can anyone help me understand what the error “non exhaustive pattern match” means?


> Like `clojure.core/case`, if no patterns match an exception will be thrown.

Jimmy Miller01:03:04

It means your pattern didn't match. If you copy your data and code here we can help


(m/rewrite [1 2 3]
  [?v ...]
  [?v ...])
Weird, this throws a "non-exhaustive pattern match" compile error


versus the error given by m/match:

(m/match [1 2 3]
  [?v ...]
Zero or more patterns may not have references to unbound logic variables.
   {:unbound #{?v}, :syntax-trace [(?v ...) [?v ...]]}
(which isn't that informative either)


Huh, just realised that syntax errors like

(m/rewrite 1 ?a) 
also produce "non-exhaustive pattern match" errors instead of complaining about a missing subst-pattern argument. I don't recall it behaving like this before?


Hmm… yeah something seems off about that.


I’ll look into it.


I figured out the cause of the bug and will release a patch shortly. The patch will resolve both of the problems you mentioned.


What would you say is best practice for picking apart a large deeply nested json (with quite a bvit of data on the first level) transform where the end result is multiple CSV’s with id’s to relate to one another. To give context my pattern and action is a total of 138 lines in a single rewrite transform. Options layed out in front of me are. • Do a single pass with rewrite, then pass that result to some matches and searches • Do one big search where the first level data is needlessly repeated in the output.. • Do the matches and searches without using rewrite • Or do all the work in the rewrite I’m leaning towards the first or last one


Oh I just thought of a neat feature that would be nice. If not already in Meander.. haha. Use my pattern as my action. For quick inspection of the result.

Jimmy Miller15:03:21

Sadly not everything the exists on the left-hand-side works on the right-hand-side. So this would only work for something things.

Jimmy Miller15:03:57

But in zeta we actually do have ability to generate data based on a pattern. So you can actually supply a pattern and have data automatically generated for you

Jimmy Miller16:03:45

I do think we need nice inspection tools for what you’ve matched though.


Cool. Yea those things you just described sound great :)


I often copy paste my pattern to my action to see what the result is before I work on more custom outputs.


How do I go about flattening this.

   {:Sales {:Overall {:TicketCount ?sales-ticket-count
                      :Amount ?sales-amount}
            :ByCategory [{:Name !category-name
                          :TicketCount !ticket-counts
                          :Amount !amounts
                          :ByReduction [{:Name !reduction-name
                                         :TicketCount !ticket-count
                                         :Amount !amount}
   {:sales-by-category [{:Name !category-name
                         :TicketCount !ticket-counts
                         :Amount !amounts
                         & [{:reduction-name !reduction-name
                             :reduction-ticket-count !ticket-count
                             :reduction-amount !amount} ..!sale-reductions]}


Such that the result looks like this.

{:sales-by-category [{:Name "category-1"
                        :TicketCount "10"
                        :Amount "100"
                        :reduction-name "student"
                        :reduction-ticket-count "7"
                        :reduction-amount "70"}
                       {:Name "category-1"
                        :TicketCount "10"
                        :Amount "100"
                        :reduction-name "senior"
                        :reduction-ticket-count "3"
                        :reduction-amount "30"}
                       {:Name "category-2"
                        :TicketCount "20"
                        :Amount "200"
                        :reduction-name "student"
                        :reduction-ticket-count "20"
                        :reduction-amount "10"}

Jimmy Miller16:03:55

If you can provide some input data as well I can more easily give you an example.

Jimmy Miller16:03:01

Although as I’m looking at your example it might be tricky. Not sure till I play with it.


Sorry. That’s my normal go to. Providing something that can be done at the repl.


Here is one you can plug straight into the repl.

(m/rewrite {:Sales {:Overall {:TicketCount 500
                                :Amount 20000}
                      :ByCategory [{:Name "Frontrow"
                                    :TicketCount 200
                                    :Amount 8000
                                    :ByReduction [{:Name "Student"
                                                   :TicketCount 20
                                                   :Amount 100}
                                                  {:Name "Senior"
                                                   :TicketCount 100
                                                   :Amount 289}]}
                                   {:Name "Middle"
                                    :TicketCount 240
                                    :Amount 8890
                                    :ByReduction [{:Name "Student"
                                                   :TicketCount 220
                                                   :Amount 103}
                                                  {:Name "Senior"
                                                   :TicketCount 105
                                                   :Amount 289}]}]}}
             {:Sales {:Overall {:TicketCount ?sales-ticket-count
                                :Amount ?sales-amount}
                      :ByCategory [{:Name !category-name
                                    :TicketCount !ticket-counts
                                    :Amount !amounts
                                    :ByReduction [{:Name !reduction-name
                                                   :TicketCount !ticket-count
                                                   :Amount !amount}
             {:sales {:ticket-count ?sales-ticket-count
                      :amount ?sales-amount}
              :sales-by-category [{:Name !category-name
                                   :TicketCount !ticket-counts
                                   :Amount !amounts
                                   & [{:reduction-name !reduction-name
                                       :reduction-ticket-count !ticket-count
                                       :reduction-amount !amount}]}


I could also be approaching this entirely the wrong way and maybe that could be handled by doing another meander transform on a subset of the data.

Jimmy Miller18:03:58

(m/rewrite data

  {:Name ?category-name
   :TicketCount ?ticket-counts
   :Amount ?amounts
   :ByReduction [{:Name !reduction-name
                  :TicketCount !ticket-count
                  :Amount !amount}

  [{:Name ?category-name
    :TicketCount ?ticket-counts
    :Amount ?amounts
    :reduction-name !reduction-name
    :reduction-ticket-count !ticket-count
    :reduction-amount !amount}

  {:Sales {:Overall {:TicketCount ?sales-ticket-count
                     :Amount ?sales-amount}
           :ByCategory [(m/cata [!categories ...]) ...]}}
  {:sales {:ticket-count ?sales-ticket-count
           :amount ?sales-amount}
   :sales-by-category [!categories ...]})

Jimmy Miller18:03:55

I couldn't think of a way of doing this without using cata.

Jimmy Miller18:03:48

Well, of course you can break it up into multiple matches. But since you want the categories to repeat, we need them not to be in memory variables. This is a good way to do that.


The trick here is to use cata on each item in the :ByReduction sequence to do the rewriting of those items before dumping their contents into :sales-by-category


OMG I just noticed Jimmy posted the same answer. 😂


I see. So it also doesn’t matter about the order?


In terms of the rule ordering?


In Jimmy’s answer he put the !categories transformations above the main pattern and action. You have it after.


Oh, no, at least not in this case.


I figured it out after looking at it for long enough and the reference one in the docs.


I was confused as to how it determined what datastructure is the output. But it’s the one making use of the variables of course 🙂


Slowly getting there lol


Awesome! 🙂

Jimmy Miller19:03:21

Yeah, in this case it doesn't matter, but it could if the patterns were overlapping. Then there are tricks to differentiate them. We use some of those tricks in zeta.


Side question. What is cata short for?

Jimmy Miller20:03:56

Basically think of it like "pretend I already did recursion and match on that". That is what we are doing here.

👍 4