Function Idiosyncrasy: Bug or Feature?

I recently came across this unexpected behavior of filter functions, which might be intended, but surprised me:

If I have a tiddler with the fields

  • title: Idiosyncrasy
  • caption: Idiosyncrasy Caption Field

Then this code

\define demo-subfilter() [<currentTiddler>]
\function .demo-function() [<currentTiddler>]

{{{ [<currentTiddler>get[caption]!is[blank]else<currentTiddler>] :map[subfilter<demo-subfilter>] }}}

{{{ [<currentTiddler>get[caption]!is[blank]else<currentTiddler>] :map[.demo-function[]] }}}

will produce this output

Idiosyncrasy Caption Field
Idiosyncrasy

Now I thought that the :map filter run prefix will set currentTiddler inside the run to the currently processed input title, which in this case should be the caption field of the tiddler. This then is the output of the subfilter. However, the filter function outputs the title, which is the value of the currentTiddler variable outside the :map run, not inside where the filter function is called.

Was I using the filter function in the wrong way?

I have set up a sharing edition TW with the code above:
Sharing Edition

Merry Christmas
Yaisog

PS: Shameless plug: I would never have figured this out in a live wiki without the debug-log Filter Operator.

1 Like

I think this is an oversight and a bug, namely that variables set by filter run prefixes are not used when evaluating functions. Suggest creating an issue on GitHub.

It’s a fabulous thing you have there. :heart:

And please don’t remove the XXX alias!

On the contrary, I still need to add this to the documentation. That was actually @pmario’s idea, if I remember correctly.

I wanted to have something that stands out from the existing filter syntax. But I did miss it that you added it. Or I did forget about it. So a bit of extra docs would be nice. Thanks for the very useful plugin!

Good catch @Yaisog and I understand your examples are to illustrate the problem. But of course they are not practical. I had not come across this problem because the functions, or the resulting custom filter operators often work as an operator, or a subfilter such that one does not need to make use of a “filter run prefix”.

  • Another trick where we use +[join[ ]] or [format:titlelist[]join[ ]] allows us to pass lists through a “custom filter operator”, also reduces the need again.

Although even more possibilities would be enabled if this is fixed.

I just want to mention this until a fix is delivered. There are most likely alternative ways to achieve the same outcome.

However if you have a real world example you can post we can assist with a workaround if your want.

My workaround is to use the subfilter for now, as given in the OP.
The application is, given a list of possible prefixes in a variable, to remove the one that matches from the input tiddlers. For each tiddler the matching prefix might be a different one from the list. Two :map runs iterate over the respective lists, and the inner one needs information about the current tiddler of the outer one.

Will you remove the titles with the prefix, or remove the prefix from the title?

I am doing the latter.

1 Like

@Yaisog I was just considering this, it was necessary for me to find a real world example so I decided to find fieldname with one of two suffixes and remove them. Perhaps this will give some inspiration?

\function if.suffix.remove() [suffix[-list]removesuffix[-list]] [suffix[-date]removesuffix[-date]] 

<$list filter="[fields[]if.suffix.remove[]] +[sort[]]">

</$list>

Actually, you don’t need the suffix[-list] as that check is included in removesuffix[-list].

My problem is that neither the number nor the string value of the prefixes is known in advance, but generated depending on the currentTiddler variable via another filter.

Thank you for your ideas, though, but I already got a solution working. The OP is about not being able to use \function in that solution. For now I am using \define instead of \function, or alternatively the fix from PR #7906.