Percent syntax idea: for loop

The 5.3.2 release documentation says about the conditional shortcut syntax:

This is the first of a new type of wikitext syntax based on tokens delimited with <% and %> .

Which makes me wonder, what more is planned (besides a case statement the docs mention)? I haven’t been able to find anything about it on Talk TiddlyWiki.

A basic idea would be a for loop:

<% for eggs in [tag[spam]] %>
I want to do something with <<eggs>>
<% else %>
I have no spam!
<% endfor %>

This would be equivalent to:

<$list filter="[tag[spam]]" variable="eggs">
<$list-template>I want to do something with <<eggs>></$list-template>
<$list-empty>I have no spam!</$list-empty>
<$/list>

I can’t be the first one to have come up with this, but I haven’t been able to find anyone else having suggested it in Talk. (Admittedly, I haven’t checked GitHub on this.)

1 Like

I think the reason you have not heard is we have become accustomed to using list widgets driven by a filter as the way to do loops. This method does not need to code an end or exit method, it ends when the filters titles end.

The %if condition is only a condition that returns true or false or something vs nothing. Strangely this is refreshingly simple. If questions are usually true and false.

I personally would be more interested in a %loop syntax, with the filter also acting on each result, if and only if, there was an opportunity to exit the loop early according to some value or condition.

This would help for an issue recently raised. Drag and drop tag pills is dangerous (tag pills can have big drag payloads) - #9 by TW_Tones

However it, a %loop, may also be an opportunity to introduce a simpler and higher performance loop mechanism with less complexity, although it would help if there is an empty message, counter variable and a count of total items value (enabling a 1st and last detector).

  • Add an exit clause
  • Technically this could use a link catcher widget as an option to the loop, rather than independent buttons etc…

You might be. The only other shortcut syntax mentioned while the <% if %> was being proposed, (I believe) was @jeremyruston’s mention of <% switch/case/default %> being a possibility down the road.

The problems I see with <% for eggs in [filter] %> are…

  1. It’s easy to produce loops (i.e. list processing code) using existing syntax which (to me) is semantically more meaningful and correct.

  2. The supplied variable (eggs here) would prevent the intrinsic/implied/automatic passing of currentTiddler, effectively destroying it downstream in that same scope.
    2.1 The dropping of eggs to bring currentTiddler back in, gives <% for in [filter] %> which reads and looks weird. Unless, perhaps, <% for-each [filter] %> was proposed along with it, which would always use currentTiddler. But again, that does nothing to address #1 and #3.
    2.2 You might want to introduce .currentTiddler as a proposed parallel variable to eggs etc.

  3. The addition of <% else %> to a list processor is contrived and semantically odd-sounding – it merely shortcuts code for the perfectly meaningful <$list-empty> (or even emptyMessage).

That’s my first take.

1 Like

So? It’s common, inevitable even, for there to be multiple common ways of doing the same thing. (For example, [all[current]] and [<currentTiddler>].) Even the <% if [...] %>... <% else %> ...<% endif %> is simple to do in a list. All you have to do is add :then[[]] (or +[first[]] etc.) to your filter and variable="_" or some such thing, and then it’s just a shortcut for the list template and list empty. The only difference is that using a list widget as a conditional is sementically confusing. However, just because using <$list> directly is semantically appropriate for looping doesn’t mean that <% for %> isn’t. So if <% %> is to be used for analogs to control flow syntax of other programming languages, then to me it seems logical to include for loops.

I was thinking <% foreach [filter] %> (without a hyphen), but exact same idea.

It’s semantically odd, but not merely contrived; I actually had Python’s for: ... else: in mind (the difference being that you would have to use break in the Python loop for the else to function the way it would in my proposal). But you could always name it <% empty %> instead of <% else %>.

That’s funny. The list-widget was developed to have an equivalent of the highly successful and versatile fet-tiddler macro used in TWclassic. fet → for-each-tiddler

I’m not sure, if there has been some discussion about the name, but the list-widget is an extended version of the fet-tiddler macro.

This drag and drop problem cannot be solved with a new loop syntax. It is a lower level problem as discussed at GH: [IDEA] It is proposted to add a "listactions" parameter to the droppable widget which acts different to "actions" · Issue #8177 · Jermolene/TiddlyWiki5 · GitHub

1 Like

I know that, and the solution you proposed is a new attribute, I am fine with that.

However it raised the possibility or need to be able to code something that allowed an exit clause, ie it starts with a list or titles, and will process everyone, however half way through one may want to be able to trigger an exit - not currently possible with the List widget. You can’t decide halfway through, that you want to stop this current “loop”.

  • Hence seeing the opportunity to provide the loop as this thread suggests and to introduce a syntax that allows an exit.

Since all wikitext syntax is ultimately implemented as widgets, I think for this use case we could focus on how to enhance the list widget before we come up with new syntax for it. Here’s one idea: a break attribute that takes a filter and ceases processing the items for any item on which the filter returns a value. I imagine something like this:

<$list filter="[filter]" break="[<item>length[]:compare:integer:lteq<ind>]" variable="item" counter="ind">
...
</$list>

This would process each item output by the filter, until it reaches an item with fewer characters than the list index. (This is a worthless and contrived example, but the point is to show the variables set by the “variable” and “counter” attributes would be available in the “break” filter.)

I suggest “break” by analogy to loops in most popular programming languages, but if we wanted to discourage that association, we could name the attribute exit instead.

2 Likes

I agree that should be considered, however it is important to note that the list widget does a lot, driving much of the core behaviour, and is arguably “overloaded”. My argument is if we add further forms similar to the <% if condition %> such as the <% loop filter %> this may be the opportunity to add an exit feature independently of the List widget. This would be in keeping with a proposed “case” element.

Although the %if syntax is fairly new, it seems to have reduced the once common requests on how to ask a simple if/then/else question. The answer was always in the use of the $list widget filters, but now people can go to a simplified option.

  • However my argument is not to modify the %if to allow loops, instead use the suggested %loop idea, although if possible an exit-filter on the list widget would be nice.

Already answered in the text you chose not to quote.

I’m not saying your idea is a bad one, I’m saying I don’t see it as having as much gravitas as the reasoning behind <% if %> (beaten out over many months on github).

I said/implied <$list> is more appropriate for list processing code. This is, of course, a call out to the ~20 years history behind it. Any notional for construct has to cover all the points I made, if it’s to help shorten <$list> code. There could be more points to cover, that was just my first stab – we’ll see how the discussion develops.

@pmario. Yep I remember FET well. Remember when it first appeared as a plugin (if I’m not confusing it with something else). Written by a German guy I think… no longer around?


'twould. 100%.

I prefer introducing .currentTiddler. That could be added now for all of $list derived code, including %if, %for, %switch etc.

Other than the (pretty horrible) workaround, to have your list body inside an %if. When the condition turns false, the $if body is skipped.

Like I said, pretty horrible. And tedious. And wastes a lot of iterations, depending.

And even then, unless I’m misunderstanding, this would only skip the current iteration without actually exiting the loop. (You’d have to make your filter find a way to not only detect the condition for exit, but also detect if that condition occurs for any previous item in the list. Probably not impossible.)

That would be Udo Borkowski. see https://tiddlywiki.abego-software.de/

-e

2 Likes

It’s true that the new if-syntax is easier to understand for users.

Technically the conditional-syntax is syntactical sugar for the list widget. If you have a closer look what the parser does with the <% if %> condition you’ll see this:

It creates a list-widget, with a list-template, list-empty and a variable named condition. That’s basically the same thing that we did with the list widget.

The case statement seems like an obvious candidate for another shortcut syntax. I do have an example of a “for” statement in my notes, but I wasn’t convinced that it was much more expressive than the list widget itself:

{%for item in items %}
    {%if item.check_something() %}
        {% set ns.found = true %}
    {% endif %}
    * {{ item.title }}
{% endfor %}

It’s interesting to think about other possibilities. I’ve looked at a few programming languages on Wikipedia to see what other primitive constructions are out there. It’s relatively easy to apply ideas like if/case/for from regular procedural languages, but I do wonder if I might be missing other constructions that are unique to our reactive declarative environment.

2 Likes

I think that’s an interesting approach. I actually like it. I’ve never seen an “else” in a “for x loop”. The problem I do have is, that it is very close to the list-widget itself. – I don’t know, it would be worth experimenting and see if the code it creates is easier to understand.

If we use it like this, we could use <currentTiddler> as the default variable.

<% for [tag[spam]] %>
I want to do something with <<currentTiddler>>
<% else %>
I have no spam!
<% endfor %>

Just some thoughts.

I think else is misleading, it is not for “each tagged tiddler” else “for each tagged tiddler”, it is else for no tagged tiddlers, the aforementioned emptyMessage is a better example.

  • To me else in the above example “should be empty”, that is for an empty for loop.

And any way should the example read more like this?;

<% for [tag[spam]] %>
I want to do something with EACH <<currentTiddler>> in the for loop?
<% empty %>
I have no spam!
<% endfor %>

This seems to be by far the most common response.

Based on this topic, I think new for syntax would also be easier to understand for new users (not that the existing list is hard), at least for those with who have touched any major procedural programming language before. It seems right now there is little demand for for loops (except that topic I found), but I think if we add case, then any new user who sees that there is if and case will wonder why there is not for or foreach, and perhaps we can revisit it then.

It exists in Python, which gave me the idea (and, it seems, the mistaken impression that the word “else” would make sense to people in this context). But perhaps <% empty %> would be better (although that might preclude another idea - one which I think is new enough to the discussion to merit a separate comment).

I think you and I are the only ones.

The pitfall I see with this is that if a filter is accepted directly after the word “for”, then for egg in [tag[spam]] becomes ambiguous, because “egg in [tag[spam]]” is itself a valid filter. You could of course have the ambiguity resolve in favor of the interpretation of naming a variable if possible, but then you couldn’t use the filter “foo in bar”. I personally think that having a separate <% foreach %> would handle this case better.

Even if true, I still don’t get why an existing way being “more” appropriate makes syntactic sugar not useful, as long as the syntactic sugar is sufficiently nice. (And higher up in this same comment, I linked to a topic where a new user was asking how they could do a for loop to show that for at least some new users, new syntax might be helpful.)

I really like this new format. I have been tinkering with refactoring the TW5-Formulas plugin to use the new <% formula input %> “syntactic sugar”. :slight_smile: Nice to see others thinking about similar things.

I would definitely use a <% for variable in [filter[run]] %> syntax.

Also, having a <% foreach [filter[run]] %> as an alias for <% for currentTiddler in [filter[run]] %> makes sense.