This is my first pass at doing this at much more naive level using the navigator you provided. Based on the output I'm clearly misunderstanding how this navigator identifies common roots.
I'm just trying to get a feel for picking out the roots by applying it to input that just uses literal variables, e.g. %1
, rather than an actual literal. But having it also expand ->>
is making me unsure whether I'm placing all the functions correctly. Regardless, they definitely should not be all stacked at the top in order of the transform
calls like that.
it doesn't identify common roots
that snippet was just to show doing a transformation that wraps after identifying the expression to wrap
Okay, gotcha. I would think the way I'm recursing on it would at least update at the next level up, though. Right now they're at the top-level. I may just be confused by the expanded ->>
though...
Yup, it's the expansion of ->>
that's throwing me. I'll pay around with this and probably come back when I have something that works but seems could be more efficient. Thanks, @nathanmarz !
I'm still a bit confused about a navigator for this. I'm currently calling transform
in multiple passes for each pattern and figure I can bypass having to find common root bindings by enforcing linearity but, e.g. (subselect TREE symbol? (selected? NAME pattern))
, navigates to a vector containing the first symbol matching the pattern rather than the coll containing that symbol.
subselect
navigates to the result of running select
on that path
(select-any (subselect ALL :a even?) [{:a 1} {:a 2} {:a 4}])
;; => [2 4]
(transform (subselect ALL :a even?) reverse [{:a 1} {:a 2} {:a 4}])
;; => [{:a 1} {:a 4} {:a 2}]
Sorry, seems I misread the docs. Is there a navigator that will act like contains?
on lists?
contains?
doesn't work on lists
since it's not keyed
I know. I suppose like some
you mean something like (selected? ALL even?)
?
I suppose if I could replace even?
with a pattern and then navigate to top-most coll containing the pattern?
I'm realizing using let
bindings for linearity throws a wrench in that, but I should be able to figure it out. Seems easier than finding common roots.
I would think I could just do (collect TREE (selected? symbol? NAME pattern))
where TREE
navigates to every coll, but then it just stops at the top level every time.
if TREE
navigates to collections, then that selected?
clause will always fail
Ah, well that does seem to be what's happening. You're implying I could have it navigate to every item? I was thinking maybe I should just use a zipper and then could specify relative paths once a pattern is matched.
user=> (setval (selected? symbol?) :replaced ['a 2 'b])
[a 2 b]
user=> (setval (selected? ALL symbol?) :replaced ['a 2 'b])
:replaced
If I can combine zipper navigators with regular ones then it would seem something like [TREE symbol? NAME pattern z/UP]
might be the simplest way to do this.
https://github.com/nathanmarz/specter/wiki/Using-Specter-With-Zippers
Yup, reading now.
I'm just uncertain whether zipper navigators can be combined with regular ones. It'd be much easier in this case than using predfns.
The answer would seem to be no based on the examples
zipper navigators operate on zipper data structures
use navigators like VECTOR-ZIP
and NODE
to navigate in and out of zippers
The structure that zippers return doesn't seem to make them ideal for use with transform
though. I'm having some luck with [ALL seq? (fn [x] some #(= % (symbol pattern)) x)]
: on the first pass it at least matches the seq
immediately above the pattern.
you generally don't navigate to a zipper data structure for the transform fn, you use NODE
to navigate into the value the zipper is currently pointing at
If I use NODE
then it again locates the pattern rather than the seq
containing the pattern, though.
So (select [SEQ-ZIP (find-first #(= % (symbol "%"))) NODE] (list (list '% 'foo)))
=> [%]
rather than (% foo)
And if I use (select [SEQ-ZIP (find-first #(= % (symbol "%"))) UP] (list (list '% 'foo)))
then it's not really in a form that I can call transform
on.
I really just want something like [TREE symbol? NAME #"%"]
except navigating to the seq containing the pattern.
Obviously I can call ffirst
on the zipper version, but that doesn't really help if I want to transform it preserving the structure it's in.
@sophiago how is [SEQ-ZIP (find-first #(= % (symbol "%"))) UP NODE]
not exactly what you want?
it navigates to (% foo)
user=> (transform [SEQ-ZIP (find-first #(= % (symbol "%"))) UP NODE]
#_=> (fn [expr]
#_=> `(:wrapped ~expr))
#_=> '(+ 1 (% foo)))
(+ 1 (:wrapped (% foo)))
I think I neglected to try that combination 😕
One sec, I'm trying it in the transform
I think that's it 🙂
Oh, it's missing patterns inside let bindings. I knew that would be an issue when I decided on linearity
I think it may just work out that the bindings are at common roots as long as I call setval
separately instead of from inside transform
fyi you can rewrite the transform
like this:
(transform
[SEQ-ZIP
(find-first #(= % (symbol pattern)))
UP
NODE
(transformed [TREE symbol? NAME (re-pattern pattern)]
(fn [_] (str fresh-var)))]
#(list `fn* [fresh-var] %)
x)
Yeah, the common roots issue is difficult and I can't get around it by using let bindings with linearity. But there's something deeper to say about what structures allow this navigator to finds them anyway and structures that cause trouble. Like I have one where a bound variable repeats and it's wrapped correctly and one where it's not. I tend to think the latter could be solved with destructuring in the example.
I think it actually comes down to currying. The one where it doesn't naturally find a root needs to be a binary function for map-indexed
. Calling first
and second
after interleaving and partitioning to use plain map
creates the same type of structure.