Recursive function not working properly when helper function it uses IS working

I was trying to create a recursive function that tested whether a tiddler—or any of its ancestors—has tag MyTag, where ancestry is found by checking the parent tag of any given tiddler, should it exist.

I couldn’t find anything simpler than a mutual recursion with a helper function, and several other threads here also do something similar, so that may simply be how its done. (If not, I’d appreciate advice on how to do this in a single function)

These are my functions, with in.hierarchy being the main one and .step being a simple helper:

\function .step(tid) [<tid>get[parent]] :map[in.hierarchy<currentTiddler>]

\function in.hierarchy(tid) [<tid>tag[MyTag]then[yes]] :else[!has[parent]then[no]] :else[.step<tid>]

The idea is that if the current tiddler has the tag, we return yes. If it has no parent, we return no. Otherwise, we use the helper function to recur on the value of the current tiddler’s parent field.

(I couldn’t do this in one go because I couldn’t find a way to combine the final else of the main function with the full-filter expression that’s stored in .step. I can’t find it at the moment, but I believe there is discussion here or in GitHub about allowing let-bindings inside filters. If it’s really happening, I can’t wait! I also should note that I know I should also do some recursive cycle-breaking to avoid any cycles such as C.parent = B, B.parent = A, A.parent = C. But I need to walk before I run. I’ll worry about that later.)

However, the main function does not properly report the results of recursive calls. It properly handles those with the tag and those without parents. But when it tries the return the result of a recursive call, it always seems to return no.

But, and here’s the head-scratcher, the helper function (which itself calls back to our main function) seems to work fine. As long as the tiddler has a parent, it reports the value correctly.

Any suggestions about what I’m doing wrong?

You can test this on the main site with RecursiveFunctionTest.json (2.2 KB), which includes the test cases along with the code in the main widget, plus FirstTiddler, which has MyTag, and SecondTiddler, which doesn’t, plus some descendants of them.

\function .step(tid) [<tid>get[parent]] :map[in.hierarchy<currentTiddler>]

\function in.hierarchy(tid) [<tid>tag[MyTag]then[yes]] :else[<tid>!has[parent]then[no]] :else[.step<tid>]

Your first :else filter run needs to specify <tid> as the initial selection to filter upon.

2 Likes

Well, D’Oh! Of course it does. Thank you very much. I stared at this far too long.

As always, thank you for your help!

Please mark the thread as resolved, if it is resolved.

I think using the helper function is a good approach in this scenario. Below is what a version without a helper function might look like but it is suboptimal in my opinion:


\function in.hierarchy(tid) [<tid>!tag[MyTag]has[parent]get[parent]] :map[in.hierarchy<currentTiddler>] :else[<tid>tag[MyTag]then[yes]] :else[<tid>!has[parent]then[no]]
3 Likes

Thank you!

That is certainly much more difficult to understand. I do follow it, but doing both the tag and has-parent tests twice is certainly suboptimal for understanding, and likely for runtime as well.

But still, thank you very much; when I tried to create that, I never thought to rearrange the clauses! It’s good to see, even if sticking with the helper version is better.

1 Like

This seems like a good place for a question I keep meaning to ask.

Is there any good way to keep helper functions like the above private?

When I tried to put it into its own tiddler and import that directly in the tiddler that has the public function (and $:/tags/Global), it doesn’t seem to work.

title: $:/_/my/functions/in.hierarchy
tags: $:/tags/Global

\import [[$:/_/my/functions/private-helpers/in.hierarchy]]

\function in.hierarchy(tid) [<tid>tag[MyTag]then[yes]] :else[<tid>!has[parent]then[no]] :else[.step<tid>]
title: $:/_/my/functions/private-helpers/in.hierarchy

\function .step(tid) [<tid>get[parent]] :map[in.hierarchy<currentTiddler>]
title: in.hierarchy tests

<<in.hierarchy FirstTiddler(I)(A) >>
<!-- yields no result -->

And it doesn’t work if I try to use the full ImportVariablesWidget either… which is not surprising, since the function pragma is no longer one of a list of pragmas that appear before anything else in the tiddler…

But everything works, of course, if I just put the helper in the same file as the public one:

title: $:/_/my/functions/in.hierarchy
tags: $:/tags/Global

\function .step(tid) [<tid>get[parent]] :map[in.hierarchy<currentTiddler>]

\function in.hierarchy(tid) [<tid>tag[MyTag]then[yes]] :else[<tid>!has[parent]then[no]] :else[.step<tid>]

I don’t believe I can nest functions inside functions, the way I can nest functions and procedures inside other procedures. (Am I wrong? When I tried, I just hung the browser tab.)


This is not critical. I can certainly live with these being defined inline. But there’s a certain uncleanliness to it. I don’t see a way around it though. Am I missing something?

There are these operators we have discussed previously to assist here Recursive filter operators to show all tiddlers beneath a tag and all tags above a tiddler - #55 by Scott_Sauyet

@TW_Tones: There is some conceptual overlap, but those serve a different purpose. Those operators start with a tag and show a tree of all tags-based children beneath them (or above them.) Here I’m simply trying to figure out for a particular tiddler whether it has an ancestor with a certain tag, using the parent field. to note ancestry relationships.

Unfortunately, I do not believe there is at present. Personally I tend to avoid global functions unless absolutely necessary, just importing function where needed and namepacing the helper functions.

Agreed. It would be very useful to be able to group filter runs à la brackets in arithmetic:


\function in.hierarchy(tid) [<tid>tag[MyTag]then[yes]] :else[<tid>!has[parent]then[no]] :else((([<tid>get[parent]] :map[in.hierarchy<currentTiddler>])))
1 Like