@noprompt @jimmy I added my first example with questions: https://github.com/noprompt/meander/pull/125/commits/556320934a6059fbff8c5bb986a1e8eb431352b2?short_path=4b806e6#diff-4b806e6fe29c6781cf8d51d441d3b4a9
I marked questions/requests for feedback with QUESTION tag inline
I saw that. 🙂
This is great by the way. 👍
meta-question: ofc lmk if there's other changes to make it easier to review. I had a bunch of examples and scrapped them once I realized I could answer all of them by looking at the tests file
ofc, i'll be going back to add them; was just way off in that first attempt
Ha! It’s funny because I’ve often thought it might be more accessible/friendly to extract some of the test examples into the cookbook i.e. the fib-test
stuff.
heh, fwiw, they were super helpful! also made me realize the docs didn't include the full api
Yeah, there’s a lot there and I generally don’t follow implementation updates, etc. with doc updates (I should, I know).
meta-comment: i think also helpful is more philosophical comment/feedback of "should meander even be used for this scenario?"
Definitely. Honestly, we have made a good faith effort to do that here.
I often find myself looking at my meander rewrite and thinking I've made it more obtuse. (Ofc as one who's learning both clojure + meander, I put weight = near 0 on my thoughts at this point)
A meander group-by
often comes up and the recommendation has always been to just use group-by
.
ah interesting; i'll have to look that up
i've also opted to put the different iterations of my meander attempts in the cookbook (both for future me's sake and learning value); ofc feel free to scrub them or maybe move them to a tutorial
Jimmy does that to great effect in some of his posts and I think it’s a nice way to guide someone from one place to the next.
With rewrite
you can do the (m/app #(->OppathSeg :seg-attr %1) !seg)
on the right side.
Someone I work with isn’t really a fan of m/app
on the left side.
If I can defer m/app
to the right side, I do.
i'd say i'm in that camp too; i couldn't figure out how to carry the "patteern match state" though to the right side
i.e. I find myself wanting to do this alot
token ::= (:arg-in|:arg-out) ?argname
pseudocode-result:: (str (emit-in ?arg-attr)|emit-out :arg-attr) ?argname)
Strings are on the list of things match/build.
I’ve been catching up on some research and experimenting with how to model, compile, interpret, etc.
sans strings, would it be possible to do that? I keep reaching basically for the rhs to apply different functions based on the pattern matched
Another thing that’s on the list is a way to aggregate.
Possible to pattern match on strings?
ah, i mean excluding the string case
Oh are you asking if the thing matches bind it to a variable?
ie in my example, i pattern match an xpath segment (ignore that it's a string). I want to apply a transform to said xpath segment depending on the type of xpath segment
Use two memory variables?
!seg-attrs
, !seg-chlds
and then apply the transforms on the right.
oh; can I use m/or
on the rhs in substitutions?
You can’t use m/or
on the right side yet.
But memory variables are always initialized to the empty vector at a minimum.
[(m/app f !seg-attrs) ... (m/app g !seg-chlds) ...]
FYI I gotta step away for a bit. Should be back in a couple hours.
sure, np! let me play with that a little
ah couldn't get it to work still. Here's a simplified snippet showcasing actual vs desired output
(defn f [xseg] {:kind :seg-attr :val xseg})
(defn g [xseg] {:kind :seg-chld :val xseg})
(m/rewrite ["oppas" "obj1" "@attr1" "@attr2" "obj2"]
(m/with [%segattr (m/pred #(= (first %1) \@) !seg-attrs)
%segobj (m/pred #(not= (first %1) \@) !seg-chlds)]
[(m/re #"obj|oppas|dc" ?ns) . (m/or %segobj %segattr) ...])
{:ns (keyword ?ns)
:xsegs [(m/app f !seg-attrs) ... (m/app g !seg-chlds) ...]}
)
;; INCORRECT: =>
{:ns (keyword "oppas")
:xsegs
[{:kind :seg-attr, :val "@attr1"}
{:kind :seg-attr, :val "@attr2"}
{:kind :seg-chld, :val "obj1"}
{:kind :seg-chld, :val "obj2"}]}
;; CORRECT =>
{:ns (keyword "oppas")
:xsegs
[{:kind :seg-chld, :val "obj1"}
{:kind :seg-attr, :val "@attr1"}
{:kind :seg-attr, :val "@attr2"}
{:kind :seg-chld, :val "obj2"}]}
Gotcha. There are two approaches you could apply here. The first one uses a helper to construct the xseg:
(defn make-xseg [val]
(m/rewrite val
(m/re #"@.*" ?val)
{:kind :seg-attr :val ?val}
(m/re #"[^@].*" ?val)
{:kind :seg-chld :val ?val}
?val
{:kind :unknown :val ?val}))
(m/rewrite ["oppas" "obj1" "@attr1" "@attr2" "obj2"]
[(m/re #"obj|oppas|dc" ?ns) . !segs ...]
{:ns (m/keyword ?ns)
:xsegs [(m/app make-xseg !segs) ...]})
;; =>
{:ns :oppas,
:xsegs
[{:kind :seg-chld, :val "obj1"}
{:kind :seg-attr, :val "@attr1"}
{:kind :seg-attr, :val "@attr2"}
{:kind :seg-chld, :val "obj2"}]}
The second uses m/cata
on the left or right side:
;; Left side
(m/rewrite ["oppas" "obj1" "@attr1" "@attr2" "obj2"]
[(m/re #"obj|oppas|dc" ?ns) . (m/cata !segs) ...]
{:ns (m/keyword ?ns)
:xsegs [!segs ...]}
(m/re #"@.*" ?val)
{:kind :seg-attr :val ?val}
(m/re #"[^@].*" ?val)
{:kind :seg-chld :val ?val}
?val
{:kind :unknown :val ?val})
;; Right side
(m/rewrite ["oppas" "obj1" "@attr1" "@attr2" "obj2"]
[(m/re #"obj|oppas|dc" ?ns) . !segs ...]
{:ns (m/keyword ?ns)
:xsegs [(m/cata !segs) ...]}
(m/re #"@.*" ?val)
{:kind :seg-attr :val ?val}
(m/re #"[^@].*" ?val)
{:kind :seg-chld :val ?val}
?val
{:kind :unknown :val ?val})
I hadn't thought about using cata on the right side before. In this example, it doesn't look like there's much advantage. Offhand, can you think of a situation where cata on the rhs is meaningfully different/important?
@e749 Thank you for adding more examples! I have tried a couple times to pick up meander, always got stuck on something. Hoping to make a push in 2-3 weeks to start using it alot more. (should go without saying thanks to Jole and the other authors too though 🙂)
@markaddleman Cata on the right side can be used to construct a value to be recursively rewritten. It’s the dual of the left.
Ah, of course. Thanks
(m/rewrite ["oppas" "obj1" "@attr1" "@attr2" "obj2"]
[(m/re #"obj|oppas|dc" ?ns) . !segs ...]
{:ns (m/keyword ?ns)
:xsegs [(m/cata ($EXAMPLE !segs)) ...]}
($EXAMPLE (m/re #"@.*" ?val))
{:kind :seg-attr :val ?val}
($EXAMPLE (m/re #"[^@].*" ?val))
{:kind :seg-chld :val ?val}
($EXAMPLE ?val)
{:kind :unknown :val ?val})
;; =>
{:ns :oppas,
:xsegs
[{:kind :seg-chld, :val "obj1"}
{:kind :seg-attr, :val "@attr1"}
{:kind :seg-attr, :val "@attr2"}
{:kind :seg-chld, :val "obj2"}]}
sure np! feel free to add feedback to the draft PRs
brilliant; both left side/right side with m/cata
feel clean and easily grokkable for future me
You can do some exotic/interesting kinds of things with this.
@jatkin Reach out when you get stuck. If I can help, I will. 🙂
Thanks, I will probably end up doing so 🙂. I'm trying to set myself up for success in https://github.com/JJ-Atkinson/Fisher project, and I think meander might be quite helpful for sections.
@jatkin hah! exactly what i'm using it (albeit targetted towards C/C++)
How so?
parse decorated c/c++ => highlevel IR => clojure does term rewriting/inline expansion/etc => codegen c/c++ & UI bindings => JIT back into the app/editor
it's more or less the lighttable dream but with c/c++ target & gamedev focus
Gacha. Sounds neat. It is/was a great piece of software.