meander

All things about https://github.com/noprompt/meander Need help and no one responded? Feel free to ping @U5K8NTHEZ
jose 2019-12-31T10:54:53.087200Z

hi, I have a question related to in place transformation, I'll try to explain it with an example. I have this data:

{:records  [{:name "A" :value 1 :foo "not-important"} 
	        {:name "B" :value 2 :bar "not-important"}]}
and I want this output:
{:records  [{:name "A" :value 0} 
	        {:name "B" :value 2}]}
for the list of records, I want to keep only some keys, :name and :value, and if :name value is A I want to set the value to 0. Right now, I'm doing it in 2 steps, the first one like this:
(m/rewrite records
   {:records [{:name !name :value !value} ...]}
   {:records [{:name !name :value !value} ...]})
and then I use specter to update some of the records. Is it possible to do both transformations currently with meander, or will be better to wait for the next release?

yuhan 2019-12-31T13:52:23.088900Z

you probably need a record-transforming function, and then use m/app to call it in the RHS of the rewrite rule

yuhan 2019-12-31T13:54:34.091500Z

`(m/rewrite data {:records [!r ...]} {:records [(m/app transform-record !r) ...]})`

yuhan 2019-12-31T13:56:59.092800Z

or maybe abuse m/cata to match on both the overall shape and each record with the same rule

jimmy 2019-12-31T16:28:11.098200Z

@jlle Here is one way to do that in meander directly. Inline:

(m/rewrite {:records  [{:name "A" :value 1 :foo "not-important"} 
	               {:name "B" :value 2 :bar "not-important"}]}

  {:records  [(m/or (m/let [!value 0] {:name (m/and "A" !name)})
                    {:name !name :value !value}) ...]}
  {:records [{:name !name :value !value} ...]})
Extracting out into a with.
(m/rewrite {:records  [{:name "A" :value 1 :foo "not-important"} 
	               {:name "B" :value 2 :bar "not-important"}]}

  (m/with [%record (m/or (m/let [!value 0]
                           {:name (m/and "A" !name)})
                         
                         {:name !name :value !value})]
    
    {:records [%record ...]})
  {:records [{:name !name :value !value} ...]})
These two are equivalent. Just sometimes if things get complicated, turning them into a with can help. There are a few things I am doing one. First I am using m/or to match on two different patterns. Then I am using m/let to say that if this pattern matches at 0 to !value. Then I am using m/and to say that :name should be “A” and to add it to !name. If you find yourself doing this a lot, you can also look into defsyntax to make something more succinct. But I would start here.

jimmy 2019-12-31T16:35:30.098900Z

And since @qythium mentioned cata. Here is the cata way of doing it

(m/rewrite {:records [{:name "A" :value 1 :foo "not-important"} 
	                  {:name "B" :value 2 :bar "not-important"}]}

  {:records [(m/cata !record) ...]}
  {:records [!record ...]}

  {:name (m/and "A" ?name)} {:name ?name :value 0}

  {:name ?name :value ?value} {:name ?name :value ?value})

👍 1
jimmy 2019-12-31T16:38:48.100900Z

I don’t think I’d call this an abuse. Actually reads fairly well. The biggest thing to watch out for is order. {:name ?name :value ?value} will match any map, even if it doesn’t have :name as a key (it will assign nil to ?name`) . So it needs to be at the end, or constrained with a m/some.

yuhan 2019-12-31T16:46:15.103400Z

heh, I called it an "abuse" because the structure didn't seem like it was recursive in the same way as eg. syntax trees or hiccup vectors that called for a catamorphism

jimmy 2019-12-31T16:49:35.105200Z

Yeah that is true. The cata will continue to recurse if there is another :records entry.

yuhan 2019-12-31T16:55:19.107100Z

using with in that above code to extract a sub-pattern is a nice motivating example for the syntax though, I've personally been scared off by "yet another %prefix" to really look into it

😆 1
jimmy 2019-12-31T17:56:42.108300Z

Finally (and yes I know this is information overload), there is really no reason you can’t just pull things out into a function.

(defn transform-record [record]
  (m/rewrite record
    {:name (m/and "A" ?name)} {:name ?name :value 0}
    {:name ?name :value ?value} {:name ?name :value ?value}))


(m/rewrite {:records [{:name "A" :value 1 :foo "not-important"} 
	                  {:name "B" :value 2 :bar "not-important"}]}

  {:records [(m/app transform-record !record) ...]}

  {:records [!record ...]})

jose 2020-01-01T21:22:44.123600Z

@jimmy thanks a lot for all the examples, no overload at all, I'm trying to learn about meander and I found all of them useful. thanks to @qythium too

jose 2020-01-01T21:23:03.123800Z

btw, cata is not documented yet, isn't it? or at least I wasn't able to find any docs

jimmy 2020-01-01T21:26:32.124Z

There is some simple doc string documentation in the cljdoc. But not enough to explain it. I should maybe write an article on it. I'm just beginning to understand it and it fairly need and not at all obvious.

jimmy 2020-01-01T21:26:49.124200Z

Glad the examples helped.