Percent syntax idea: for loop

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.

So one of the common responses/objections is that the existing list widget is a perfectly good and already very easy way of doing loops (which is true) and therefore that new syntax for it doesn’t really add much value.

The only difference between this sort of proposed “for loop” and the “conditional/if” would be the removal in the generated list widget (in the parse tree) of “limit=1” (and “variable=condition” - removed entirely in foreach, and replaced with a different variable name in for foo in [...]). This is part of what made the idea appealing to me; I have sometimes thought, “The thing I want to do is almost exactly the same as the “if”, so why do I need to write completely different syntax for it?”

But I realized that the similarity of the widget (parse tree) implementation–the fact that conditionals and loops are just slight variants on the same thing (even the <% if %> is just syntactic sugar for a <$list> as @pmario pointed out)–would actually allow a structure unique to TiddlyWiki. If you can change a keyword to determine whether you loop or only render once, then you could extend that idea to “elseif” by introducing “else-for” (and “else-foreach”) when you want to change an “elseif” to a loop. You could even mix them:

<% if [filter] %>
some stuff
<% else-for egg in [tag[spam]] %>
if the condition is not true, we process *each* egg
<% elseif [some other condition] %>
Something to do if some other condition is true, only if there are no eggs in spam
<% else %>
If all else fails...
<% endif %>

Each “elseif” or “elsefor” block is just a $list widget (with slightly different attributes, most importantly the presence or absence of limit="1") inside of the $list-empty of the previous block.

One potential downside to this is that it would pose a challenge to the use of empty instead of else for loops. I think the strength of this sort of construction is in utilizing the fact that both “if” and “for” are basically the same thing and treating them as such, but in treating them as such, it would be hard to then say you have to use “empty” with “for” and “else” with “if”, and in mixing them, it would get confusing (do you need “empty-if” and “empty-for”???). Part of the benefit of the idea is being able to change just one key word in order to switch between looping and not looping, but requiring empty for loops and else for conditionals would make you change two keywords in two different places if you wanted to change from a loop to a conditional or vice versa.

Perhaps one way to mitigate that would be to simply let “else” and “empty” be interchangeable, both valid keywords in both loops and conditionals, with the expectation that most people are going to use the word that semantically makes most sense to them (which will usually be “else” in conditionals and “empty” in loops - though the code won’t break if you change a conditional to a loop or vice versa and forget to change the else/empty part). This would allow “for” to be taught/documented with examples using “empty” rather than “else”, and then we could have a separate page in the documentation for nesting and mixing with for loops.

   

:dart:

That’s a great point. There are more than a few (started out as) primitives in TiddlyWiki that are now quite overloaded, making the documentation daunting for n00bs and doc authors alike.