Fork me on GitHub
#specter
<
2017-03-15
>
mars0i03:03:09

I'm confused about how continue-then-stay works, for example in (continue-then-stay MAP-VALS p) from a recent example in this channeel.

mars0i03:03:24

I'm also a bit confused about recursive-path, although I've used it successfully by modifying an example in the "missing piece" blog post. In (recursive-path [] p (if-path vector? [ALL p] STAY)), what is p bound to when it finds a vector, and why is p put after ALL? (Also, what's the meaning of second argument []?) recursive-path seems very useful, but I gather it's new since it doesn't have a docstring or other documentation yet that I've found.

oskarkv10:03:11

Apparently I don't understand how not-selected? works. (select [:a (not-selected? :c)] {:a 1 :b {:a 1 :b 2}}) I expected to return something since :c does not exist. "Stops navigation if the path navigator finds a result. Otherwise continues with the current structure." It couldn't have found :c, so the current structure is 1, because that's what just :a gives?

nathanmarz10:03:55

@oskarkv but :c always navigates to something

nathanmarz10:03:59

in this case to nil

nathanmarz10:03:15

if you do (must :c), that only navigates if :c exists in the map

nathanmarz10:03:44

@mars0i continue-then-stay does literally what it says

nathanmarz10:03:50

first it navigates to the given path

nathanmarz10:03:57

then it navigates to itself

nathanmarz10:03:56

(transform (continue-then-stay STAY) inc 1) will return 3, because it navigates to itself, then navigates to itself again

nathanmarz10:03:48

p in that example is bound to itself (aka the entire path definition given )

nathanmarz10:03:24

so for TREE-VALUES, what it's saying is: if navigated at a vector, navigate to each element of the vector and recurse

nathanmarz10:03:49

otherwise (meaning not at a vector), just stay navigated at that point (finish recursing)

nathanmarz10:03:24

the [] argument i used to specify arguments to the resulting path

nathanmarz10:03:37

basically the result of recursive-path will be a function if any arguments are provided

oskarkv10:03:55

Oh, OK. Thanks!

nathanmarz11:03:49

@oskarkv oh just noticed you were navigating to :a first, don't think that path is what you want

oskarkv12:03:31

I was just playing around to learn 😛

mars0i14:03:11

Thanks @nathanmarz. That's very helpful. I'm still trying to get my head wrapped around some things that aren't intuitive for me yet. I still don't understand what p is doing in if-path branches in recursive-path. Here's an example I've been using:

mars0i14:03:54

(def a {:a1 {:b1 {:c1 1, :c2 2}, :b2 {:c1 3, :c2 4}},
 :a2 {:b1 {:c1 5, :c2 6}, :b2 {:c1 7, :c2 8}}})

mars0i14:03:31

(select [(recursive-path [] p (if-path map? [MAP-VALS p] [STAY]))] a) produces [1 2 3 4 5 6 7 8], i.e. the leaf node values. Why do I follow MAP-VALS with p? Leaving out p just produces the original map wrapped in a vector, so I know I need p, to get the leaf nodes, but I don't understand why. Normally, if I have a path operator like MAP-VALS in a vector, it's the first navigation operation, and then the next one is applied, and so on. Here it feels as if p is an argument that's passed to MAP-VALS, maybe. Is that correct? So that [MAP-VALS p] within recursive-path (or if-path?) is semantically different from, a sequence of navigators immediately after select or transform?

nathanmarz14:03:00

no, p is just the next navigation to do after MAP-VALS

nathanmarz14:03:18

think of it just like regular recursion

nathanmarz14:03:21

(defn foo [i]
  (if (= 0 i)
    1
    (* 2 (foo (dec i)))))

mars0i14:03:23

What is the value of p?

nathanmarz14:03:37

foo refers to itself there, just like how p refers to the overall path

nathanmarz14:03:48

p is (if-path map? [MAP-VALS p] [STAY]))

nathanmarz14:03:18

the effect of using p there is to continue going to all map vals until a non-map is encountered

mars0i14:03:35

I know I might seem dense. I'm just not in the mindset yet, but want to be.

nathanmarz14:03:47

(btw you don't need the [] if it's just a single navigator)

nathanmarz14:03:56

the second branch of if-path can just be STAY

nathanmarz14:03:44

are you comfortable with recursion with functions?

mars0i14:03:12

Ah, maybe I see. So recursive-path doesn't in itself cause recursion to occur. It just sets up a context in which it can occur. The recursion is implemented by using the p that it binds, in if-path.

nathanmarz14:03:19

that's right

mars0i14:03:28

Yes, no problem with recursion. It's a reasonable question to ask.

nathanmarz14:03:37

I see where you were confused now

mars0i14:03:41

The Little Lisper set me straight decades ago.

nathanmarz14:03:10

recursive-path does absolutely nothing except provide you with an object that references itself

nathanmarz14:03:31

(recursive-path [] p [ALL even?]) is the same as [ALL even?]

mars0i14:03:39

OK. That helps a lot. Thanks. It's just like a special kind of let.

nathanmarz14:03:08

if you're curious, it's actually a wrapper around the lower level local-declarepath and providepath

nathanmarz14:03:17

those two can be used to make mutually recursive paths

mars0i14:03:03

"sort of" yeah that's why I wrote "like" and "kind of" 🙂 . OK, I'll look at those at some point.

mars0i14:03:58

I'm not sure I understand continue-then-stay followed by p within recursive-path, but I think I should think and experiment a bit and see if I can figure it out before asking further questions. Thanks very mush.

nathanmarz14:03:34

no problem, happy to help

mars0i14:03:36

much. now that I understand recursive-path better.

mars0i17:03:39

Based on an answer to vikeri a week ago:

b ;=> {:b1 {:c1 1, :c2 2}, :b2 {:c1 3, :c2 4}}

(select [(recursive-path [] p
           (if-path map?
	         (continue-then-stay MAP-VALS p)))
         MAP-KEYS]
	b)
;=> [:c1 :c2 :c1 :c2 :b1 :b2]

mars0i17:03:02

Just want to check my understanding of how this works.

mars0i17:03:07

If the current element is a map, then get its vals and continue with the path, p, but also, return the current element and pass it to MAP-KEYS. So what keeps coming out of all that are the keys, and they are what are wrapped in vector. Something like that?

mars0i17:03:59

Or rather MAP-VALS p causes p into which we recurse to be bound to the outputs of MAP-VALS. But also the entire map element at that point is passed to MAP-KEYS.

nathanmarz17:03:21

better to think of it one step at a time

nathanmarz17:03:45

@mars0i the recursive-path part navigates to all maps reachable via navigations to map vals

nathanmarz17:03:57

then for each map navigated to, it navigates to the keys

nathanmarz17:03:31

because of the continue-then-stay, transformations happen first on more descendant maps

nathanmarz17:03:43

whereas with stay-then-continue the opposite would be true

mars0i17:03:39

Ah, cool about stay-then-continue. I didn't want the keys in reverse order.

mars0i17:03:11

Maybe what I wrote applies better to it.

mars0i17:03:40

Thanks again.

nathanmarz17:03:48

thin wrappers around multi-path

mars0i17:03:31

OK, I see. This is also helping me understand multi-path.