Closed yaisog closed 1 year ago
A :then
filter run prefix was in fact the first one I wrote and that eventually provided the impetus to introduce filterrunprefix
modules, allowing the addition of prefixes without needing to override core code. You can find an implementation here, though it needs updating with some of the improved handling for variables. Note that it also provides two variables to the run __input
as a stringified representation of the previous runs output and __count
.
Since no one else has previously expressed the need for this prefix, and I have had some doubts regarding these variables and how necessary this is for the core, I have never created a PR.
I believe it is hardly possible (at least without text substitution) to create something like that without
:then
.
You can use :map
as a stand in for :then
if you write your first filter run such that it produces only a single output item or none.
You can find an implementation here
Hi @saqimtiaz, I remember you hinted at an implementation that you already had, but I never found it and was too shy to ask. 😝
But as with most things TiddlyWiki, I just rolled my own. It's a straight adaptation of the :else
FRP, without the additional variables that yours provides. I can see how they might be useful, though I wouldn't need them here.
You can use :map as a stand in for :then if you write your first filter run such that it produces only a single output item or none.
For the search example above I need the input to the :then
filter run to be the same as the input to the subfilter operator (a list of tiddlers to search). I don't think this can be accomplished with :map
, unless I put that list into another variable that the :map
filter run then enlist
s as first operation.
Did you try it with the :filter
prefix instead of :then
Hi @pmario, no I didn't.
I think the input to the :filter
run would be the output of the preceding run and thus either nothing or "all". The tiddler "all" does not exist, and searching its title for searchTerm
is not going to give me the intended result.
@kookma: Feel free to describe your use cases if you have good ones.
I highly support having :then
filter run prefix. You may achieve the result you want by other complex and lengthy filter runs, BUT having :then
helps to write more readable/understandable/maintainable wikitext (script) here.
One of the best example is the one given by @saqimtiaz answering my question in the Talk
<$list filter="[<__userinput__>match[1]] :then[subfilter<noteFilter>] :else[subfilter<journalFilter>]">
I provide more examples below.
Another example
The old way:
\define select-between-two-filters(cond:yes)
<$list filter='
[<__cond__>match[yes]]
:map:flat[all[tiddlers]tag[Learning]first[10]] :else[tag[HelloThere]first[3]]
'>
</$list>
\end
<<select-between-two-filters no>>
---
<<select-between-two-filters yes>>
New way: This is much more readable if it can use :then
\define select-between-two-filters(cond:yes)
<$list filter='
[<__cond__>match[yes]]
:then[all[tiddlers]tag[Learning]first[10]] :else[tag[HelloThere]first[3]]
'>
</$list>
\end
Note that it also provides two variables to the run
__input
as a stringified representation of the previous runs output and__count
.
This is a clever implementation and looks a TW way of if-then-else.
What I learned in other language like Python/Fortran/Julia the then and else are independent from condition checked by if-clause
if x>3 then y=10
else y=-15
So I though likewise in Tiddlywiki
{{{ [tag[Tasks]] :then[all[tiddlers]prefix[Task]get[due]!sort[]] :else[all[tiddlers]tag[Done]]
But what your implementation of :then
not only covers this case but also acts on input from upstream run (a TW way)
@kookma: Feel free to describe your use cases if you have good ones.
A general form can be
<$list filter="[conditional-filter] :then[subfilter<trueFilter>] :else[subfilter<falseFilter>]">
...
</$list>
The first filter output if is not empty, :then
part shall be run in all other cases (e.g. empty in TW) the :else
part shall be run.
This is an example of above syntax in the current release (without :then
)
<$let decisionMacro= {{{ [<currentTiddler>prefix[who]then[trueMacro]else[falseMacro]] }}}>
<$macrocall $name=<<decisionMacro>> />
</$let>
each macro has its own list and filter to do the job! This syntax may look clear and understandable, but it has duplication of code and lengthy!
The first filter output if is not empty,
:then
part shall be run in all other cases (e.g. empty in TW) the:else
part shall be run.
There is the one caveat that if the :then
run results in an empty list, the :else
part will also run, unless we design :then
in a way that the filter run only replaces its input if it has a non-empty output and otherwise passes the input along, so that :else
will not trigger. This might not be what subsequent runs expect, though. Also, one could put an else
operator at the end of the :then
run to guard against this...
@saqimtiaz: How does your implementation handle this case?
Here is another application in which a :then
prefix will make the filter code look nicer:
When or
-combining two conditions that should result at maximum in one execution of a $list
s content, we would normally do somehing like
<$list filter="[«condition1»] [«condition2»] :and[then[yes]]" variable="void">
or maybe :and[first[]]
.
This would look better if we could use :then[[yes]]
in the final run, wouldn't it?
I'd be happy to have a :then
filter run prefix in the core.
I support this initiative as well because If then else is an important structure even for new users.
The special values Infinity and -Infinity can be used to represent positive and negative infinity respectively
Let me start with two relevant quotes by @saqimtiaz
and @Jermolene
Both of which I wholeheartedly agree with.
With this in mind, I would like to propose adding a
:then
filter run prefix, akin to:else
and thethen
filter operator.An example usage would be a subfilter defined via pragma:
I believe it is hardly possible (at least without text substitution) to create something like that without
:then
. Thethen
operator won't work, because the "parameter" would be a filter run. Other combinations of subfilters are similarly fruitless, and become increasingly difficult to read. I could probably wrap the subfilter call in a$set
to define the subfilter, but I don't want to do that in this case, as I'd like to import and reuse the\define
s. There are many such\define
s, which makes using$importvariables
with$set
s impractical.The implementation would be straightforward: If there is input, replace it with the output of this filter run. The input to the filter run should be
all[tiddlers]
, or for subfilters the input to the subfilter, like:else
. A debatable point might be if an input of a single empty string should count as empty input, but this must be handled the same as:else
, so there is no debate.@kookma: Feel free to describe your use cases if you have good ones.
PS: I realized after the fact that the example subfilter is unintentionally clever. 🤓 If the
:then
run is executed but yields no results, the:else
path will also be executed. But since it checks only a subset of the fields, it must also come up empty and so give the correct (empty) result. It wouldn't have worked the other way around wheretitle
andcaption
were checked first. Upon an empty result the search through all fields would have automatically executed and might have yielded false positives.