Fork me on GitHub
#meander
<
2020-02-28
>
yuhan04:02:41

(m/rewrite (range 100)
  (!a !b ...)
  {& [[!a !b] ...]})
;; => {0 1, 2 3, 4 5, 6 7, 8 9, 10 11, 12 13, 14 15}
!!!

yuhan04:02:13

this looks to be the problem.. transients aren't meant to be "bashed in place"

yuhan04:02:03

(let [t (transient {})]
  (doseq [a (range 100)]
    (conj! t [a a]))
  (persistent! t))
;; => {0 0, 1 1, 2 2, 3 3, 4 4, 5 5, 6 6, 7 7}

noprompt05:02:17

@qythium I’m confused… what is the problem?

yuhan05:02:33

I would expect the output to be a 50-element map

noprompt05:02:02

Oh, oh, yes that is a problem.

noprompt05:02:48

Do you have an idea of how to fix this? zeta already has a fix for this.

yuhan05:02:10

I think by using a loop/recur with the transient collection as a loop variable

noprompt05:02:38

Sounds good to me.

noprompt05:02:08

Would you like to patch it?

noprompt05:02:08

I learned something new about transients today.

noprompt05:02:08

I still don’t know what the hell the Clojure page on transients means by “Don’t bash in place” :man-shrugging:

yuhan05:02:53

I think basically it means don't treat it like a mutable collection

yuhan05:02:48

the conj! , assoc! etc. functions aren't like imperative statements in other languages

noprompt05:02:12

Yeah I just found some blog post talking about that…

noprompt05:02:19

Geeze… talk about a hot stove.

yuhan05:02:50

yeah, I made a similar mistake when first learning about transients

yuhan05:02:38

Okay, I changed the compile* multimethod for :rp* which fixed the above problem

yuhan05:02:45

although I have no idea what any of the internals work and how to run tests?

yuhan05:02:57

and there seem to be a few more uses of transient in the project

noprompt05:02:58

A few of those are in those pseudo rewrite rules.

noprompt05:02:28

Looks like :rp+ is probably busted too.

noprompt05:02:21

LOL and you just replace let with loop and it fixes it! 😂

yuhan05:02:44

yup, just opened a PR

noprompt05:02:59

Cool. Those rewrite rules can probably be toasted.

noprompt05:02:52

I’ll just take the example you gave and make that the test.

noprompt05:02:59

New release in just a moment…

yuhan05:02:33

give me a minute, rewriting :rp+ as well

yuhan05:02:43

ah, that was fast

noprompt05:02:38

I got rpm and rpl while I was in there.

noprompt05:02:34

As I’m working on zeta I use epsilon from :local/root

noprompt05:02:18

So if I find bugs with epsilon (which I’m using to build zeta ) I can patch them as I come up. Lately, I’m relying more on others to help catch/fix bugs. 🙂

noprompt05:02:32

@qythium The new version should be up any moment now.

[meander/epsilon 0.0.397]
Fantastic stuff! Thanks much. 👍

yuhan05:02:56

awesome 🙂 looking forward to zeta!

noprompt05:02:09

Few months 🤞

noprompt05:02:14

The things I’m most exited for are bit/byte/string matching, aggregation, and data generation.

yuhan05:02:28

I'm not sure if your changes to rp+ etc. are technically correct - still looks like in-place modification

yuhan06:02:53

the way I see it is, treat conj! exactly like you would use conj

noprompt06:02:08

Hmm…

(t/is (= (r/rewrite (range 20)
             (!a !b ...)
             {& [[!a !b] ..20]})
           {nil nil, 0 1, 4 5, 6 7, 12 13, 2 3, 14 15, 16 17, 10 11, 18 19, 8 9}))
is passing. If you’re concerned though and have doubts about the implementation, I’d be happy to merge another patch.

noprompt06:02:23

I have zero problems with that. 🙂

noprompt06:02:05

Oh I think I see what you mean.

yuhan06:02:17

I don't know if there is a counterexample for those cases, it might just happen to be working due to "undefined behavior"

noprompt06:02:35

Instead of

(conj! ,,,)
(conj! ,,,)
,,,
you’re suggesting
(conj! (conj! ,,,))
yes?

yuhan06:02:29

pass an accumulator in a reduce instead of (do (map... ))

noprompt06:02:40

Okay that just amounts to changing the map in to a reduce

noprompt06:02:28

I do need to hop off here for a bit and can bang on it when I’m back which should be in a hour or so. If you wanna hack on it thats cool too. 👍

👌 4
niclasnilsson20:02:18

Hi everyone! I’m trying to reshape a map with lists in it using meander, but haven’t found a way. Can this be done using only meander constructions?

(def data
  {:first-name "Jimi"
   :last-name "Hendrix"
   :guitars
    [{:brand "Fender"
      :model "Stratocaster"
      :year 1963}
     {:brand "Fender"
      :model "Stratocaster"
      :year 1965}
     {:brand "Gibson"
      :model "Les Paul Custom"
      :year 1955}]})

(m/search 
  data
  {:first-name ?first-name
   :last-name ?last-name
   :guitars (m/scan {:brand ?brand})}

  {:firstname ?first-name
   :surname ?last-name
   :make ?brand})

;; Result
({:firstname "Jimi", :make "Fender", :surname "Hendrix"}
 {:firstname "Jimi", :make "Fender", :surname "Hendrix"}
 {:firstname "Jimi", :make "Gibson", :surname "Hendrix"})

;; Result I would like to have. But how?
{:firstname "Jimi"
 :surname "Hendrix"
 :guitars
  [{:make "Fender"}
   {:make "Fender"}
   {:make "Gibson"}]}

jimmy20:02:31

You can use rewrite to do this.

(m/rewrite
  data
  {:first-name ?first-name
   :last-name ?last-name
   :guitars [{:brand !brands} ...]}
  {:firstname ?first-name
   :surname ?last-name
   :guitars [{:make !brands} ...]})

jimmy20:02:56

Search is going to give you back multiple possible results. If you just want one result match/rewrite is the thing you are looking for.

👍 4
noprompt20:02:06

I think the only difference between what I posted and deleted was the use of m/gather to grab the brands.

👍 4
noprompt20:02:37

This is what I love about this though… converging on an otherwise identical solution quickly instead of figuring out what functional programming combo is better than another. 😂

niclasnilsson20:02:39

Awesome, thanks @jimmy and @noprompt!

👍 4
🙌 4
niclasnilsson21:02:16

A followup question, can the gathering be done in several “levels”?

;;;
;;; Map with lists with maps with lists.
;;; 

(def data
  {:first-name "Jimi"
   :last-name "Hendrix"
   :guitars
    [{:brand "Fender"
      :model "Stratocaster"
      :details [{:year 1963 :origin "USA"}
                {:year 1965 :origin "Mexico"}]}
     {:brand "Gibson"
      :model "Les Paul Custom"
      :details [{:year  1955 :origin "USA"}]}]})

(m/rewrite
  data
  {:first-name ?first-name
   :last-name ?last-name
   :guitars [{:brand !brands 
              :model !models 
              :details
              [{:year !years
                :origin !origin} 
               ...]}
             ...]}

  {:firstname ?first-name
   :surname ?last-name
   :guitars [{:make !brands 
              :model !models 
              :manifactured
              [{:year !years
                :country !origin}
               ...]}
             ...]})


;; What I get

{:firstname "Jimi",
 :surname "Hendrix"
 :guitars [{:make "Fender",
            :model "Stratocaster"}],
            :manifactured [{:country "USA", :year 1963}
                           {:country "Mexico", :year 1965}
                           {:country "USA", :year 1955}],}

;; What I'm aiming for

{:firstname "Jimi"
 :surname "Hendrix"
 :guitars
  [{:make "Fender"
    :model "Stratocaster"
    :manifactured [{:year 1963 :country "USA"}
                   {:year 1965 :country "Mexico"}]}
   {:brand "Gibson"
    :model "Les Paul Custom"
    :manifactured [{:year  1955 :origin "USA"}]}]}

niclasnilsson21:02:30

I managed to solve it using (m/app). Is this the best/ideomatic way or is there a smarter/cleaner way?

(def data
  {:first-name "Jimi"
   :last-name "Hendrix"
   :guitars
    [{:brand "Fender"
      :model "Stratocaster"
      :details [{:year 1963 :origin "USA"}
                {:year 1965 :origin "Mexico"}]}
     {:brand "Gibson"
      :model "Les Paul Custom"
      :details [{:year  1955 :origin "USA"}]}]})

(defn rewrite-guitar-details [guitar-details]
  (m/rewrite 
    guitar-details
    {:year ?year
     :origin ?origin}

    {:year ?year
     :country ?origin}))

(defn rewrite-guitar [guitar]
  (m/rewrite 
    guitar
    {:brand ?brand
     :model ?model
     :details [!details ...]}

    {:brand ?brand
     :model ?model
     :details [(m/app rewrite-guitar-details !details) ...]}))

(m/rewrite
  data
  {:first-name ?first-name
   :last-name ?last-name
   :guitars [!guitars ...]} 

  {:firstname ?first-name
   :surname ?last-name
   :guitars [(m/app rewrite-guitar !guitars) ...]})

jimmy21:02:44

(m/rewrite
  data
  {:first-name ?first-name
   :last-name ?last-name
   :guitars [{:brand !brands 
              :model !models 
              :details
              [{:year !years
                :origin !origin} 
               ..!n]}
             ..!m]}
  {:firstname ?first-name
   :surname ?last-name
   :guitars [{:make !brands 
              :model !models 
              :manifactured
              [{:year !years
                :country !origin}
               ..!n]}
             ..!m]})
By default ... in substitution is going to put out all values. You can control how many it does by using ..!n or ..?n. https://cljdoc.org/d/meander/epsilon/0.0.397/doc/operator-overview#repeating-with-varxiables

jimmy21:02:11

(also you have a typo with manifactured)

niclasnilsson21:02:19

Great, will try that! Thanks again @jimmy!

niclasnilsson21:02:54

Meander is freaking awesome!

🙂 4
🎉 4
❤️ 4