Fork me on GitHub

Some good news: The next release of Meander will be generally faster! The work I’ve been doing to put optimizations behind flags has lead me to address a number of related gaps in the current implementation. The match compiler (e.g. the macro expansion), and map and set pattern matching have received some much needed attention over the past few weeks. I’m really excited to get these updates out.


To throw some numbers out, currently on my machine it takes somewhere between 900 and 1000ms to run the Clojure test suite. On the epsilon-compiler-flags branch the same test suite runs in between 580 and 650ms. Thats a significant improvement.

parrot 15

I think this is post compile e.g. post macro expansion but I don’t know for certain. If anyone knows let me know because I’m too lazy to look at the moment. 🙂


hi there! I have a question about some memory variable behavior that I find a little confusing. I'm trying to pattern match by a predicate and then put all matches into a hiccup data structure. Here's a basic example, adapted from the documentation:

(let [!s ["a" "b" "c"]] (m/subst [:ul . [:li !s] ...]))
=> [:ul [:li "a"] [:li "b"] [:li "c"]]
However, when I try to extract the same memory variable as defined by m/scan , it behaves differently in the context of m/rewrites:
(m/rewrites [1 2 "a" "b" "c"]  (m/scan (m/pred string? !s)) 
                                    [:ul . [:li !s] ...])
=> ([:ul [:li "a"]] [:ul [:li "b"]] [:ul [:li "c"]])
I assume this is because m/rewrites always expects to return a sequential collection and thus ignores the head/tail delimiting with . and ... that works in the context of m/subst. m/rewrite doesn't work here because it only returns the first value of the memory variable. I'm not quite sure how to proceed - what method should I be using instead? Any guidance is much appreciated!


@afoltzm is this the result you are looking for?

(m/rewrites [1 2 "a" "b" "c"]
  (m/seqable (m/or (m/pred string? !s) _) ...) 
  [:ul . [:li !s] ...])
;; =>
([:ul [:li "a"] [:li "b"] [:li "c"]]
 [:ul [:li "a"] [:li "b"]]
 [:ul [:li "a"] [:li "c"]]
 [:ul [:li "a"]]
 [:ul [:li "b"] [:li "c"]]
 [:ul [:li "b"]]
 [:ul [:li "c"]]


In situations where you want to find all the possible ways to project some values into a memory variable you’ll want to use the (m/or pattern-with-mem-vars _) technique. Memory variables, unlike logic variables, are always initialized which is why you can use them like this.


Note, you can replace m/seqable with [,,,] etc.


this is what really made it click for me - remembering that I'm rewriting a sequence and therefore I need to match a pattern within that sequence.


@noprompt I'm only interested in the first result. is there a simpler way to express the case with the maximal set of results, or should I just take the first value? thanks for the quick response!


Using rewrite in the above example will yield

[:ul [:li "a"] [:li "b"] [:li "c"]]


We try and respond as quickly as we can here. 🙂


I knew there had to be a way with m/rewrite even if I wasn't sure what it was. It's taken me a fair bit to wrap my head around term rewriting, but the old Alan Perlis quotation comes to mind as I work through the examples - "A language that doesn't affect the way you think about programming, is not worth knowing." Thanks again.


It definitely has that effect. Maude, TXL, and StrategoXT, to name a few, influenced me and fundamentally changed my thoughts on what program is and could be.


Having Meander in Clojure has been personally interesting to me because it wasn’t until I was able to get it off the ground that I could start messing with the ideas in a different setting: the REPL.


BTW, for anyone interested, have a look at those languages if you have the spare time to do so. Maude, in particular, was really interesting to me.


I’m hoping that within the next year or so we can get zeta off the ground and have some of the power of model checking in Clojure.


this is what really made it click for me - remembering that I'm rewriting a sequence and therefore I need to match a pattern within that sequence.


One more question. I want to do the following: 1. For every item in a sequence: 2a. If that item matches a predicate with m/pred, split that item into a subsequence with m/app, collect the items in that subsequence into a memory variable !is , and then insert those values into the enclosing sequence in order. (e.g. something like (m/app #(clojure.string/split % #"|") "a|b|c") ) 2b. If that item doesn't match a predicate, leave it as-is in the same position.

Jimmy Miller19:06:42

(m/rewrite ["ad,sf" 1 "asd,fa,sdf" 2 3]
    (m/and (m/pred string?) (m/app #(clojure.string/split % #",") [!xs ...]))
    !xs) ...]
  [!xs ...])

;; =>

["ad" "sf" 1 "asd" "fa" "sdf" 2 3]
@afoltzm Is this what you mean?

💯 3

I guess I still don't fully understand how the combinators in meander work because I didn't realize you could use m/and to combine a predicate with a function application. Thanks for clarifying!


You can also do (m/pred string? (m/app ,,,)))

Jimmy Miller19:06:12

All I did was do the little ‘and’ and ‘or’ trick to emulate an if statement. Used to have to do it all the time in complex (and terrible) sql stored procedures. and and or in meander work just like their logical counterparts.

(map (fn [x]
        (and (string? x) (clojure.string/split x #","))
     ["ad,sf" 1 "asd,fa,sdf" 2 3])