Template talk:Universal page list with custom separators
Not a sustainable approach to templates
[edit]@Grufo: On the one hand, this is cool. I like programming, and Module:Params is obviously super powerful. But it's not a good choice for a place like Wikipedia. It's simply too complex. Basically, you've created another programming language. When I see stuff like entering_substack, I think of Forth (programming language). :-) The main problem is: Nobody but you will be able to maintain this template. But Wikipedia is based on collaboration by (mostly) non-expert volunteers. We need code that is readable and writable by a large number of users. We should stick with Lua modules and Mediawiki templates, and the code should be kept relatively simple. I'm sorry, and I admire your work on Module:Params, but I don't think it's going to be a good fit. — Chrisahn (talk) 14:17, 16 October 2025 (UTC)
- Hi Chrisahn. I hear your points. But I believe part of the difficulty of this particular template is that involves regular expressions. However these would be the same independently of what module we used (there would be the exact same regular expressions if instead of Module:Params we used Module:String). The idea behind Module:Params is indeed a bit that of giving a bit of Lua's power to template editors without having to program in Lua – often with even more optimized/efficient results. More than a new programming language I would say “a very rudimental proto-language” or “a rich set of directives”. In the module I used plain English for all the directives and the documentation is pretty rich, so dealing with it should be easier than dealing directly with Lua. While it is true that I am the one who created the templates that exploit the module's powers in full, I am not the only one able to use it (e.g. #1, #2, #3, #4, #5, #6, etc.). P.S. That module was born on Latin Wikipedia, because there nobody knows Lua. Before Module:Params there were basically no modules there (still there aren't many), and so everything was extremely painful until Module:Params became the game changer. --Grufo (talk) 14:48, 16 October 2025 (UTC)
- Most of the examples you mentioned are one-liners, e.g.
{{#invoke:params|concat_and_call|autnum|plainlink=yes}}. I don't understand that code at the moment, but looking up the meaning of the parameters would probably only take a few minutes. I also have to do that when I want to understand other templates. No big deal. I guess module:params is fine for such relatively simple cases. But when I look at the code of Template:Universal page list with custom separators, I'm lost after at most five lines. And I've been reading and writing code for several decades, in dozens of different programming languages. :-) I expect that most other editors will have similar problems with such complex code. Something as powerful as this template should be written in Lua. The Lua code will be relatively simple, pretty much like Module:Pagelist. I'm pretty sure there are many editors here on enwiki who could easily modify the functionality of that module. But hardly anyone will be able to understand this template. :-( — Chrisahn (talk) 15:07, 16 October 2025 (UTC) - Regarding regular expressions: I don't think that's the main problem, but if we wanted to avoid regular expressions, we could probably use trim and startswith from Module:String2. — Chrisahn (talk) 15:22, 16 October 2025 (UTC)
- But Module:Pagelist is 106 lines of code. This template instead is only 36 lines. One point of Module:Params is that you can do simple things or complicate things with it. The module cannot do logic (there is nothing like if/else). The approach is closer to CSS than to a programming language. What this template does might actually be easier than it looks (regular expressions are indeed difficult, at least for the eye – but these don't depend on what tool you use). The
entering_substackdirective simply means “make an exact copy of the current parameters and work with them separately”. Thedetaching_substackdirective means “remove these copied parameters from the parent stack” (so basically what a moment earlier was a copy now has become a move of a reduced subset). Normallyentering_substackis closed bymerging_substack, however in this caseleaving_substackis used, which delays the merging until theflushingdirective is used. So, explained, what the template does is simply:- remove the first three sequential parameters
- remove empty parameters and parameters that contain only “#”
- set the three separators to
{{{1}}},{{{2}}}and{{{3}}} - detach the group of parameters that contain “#” and make them look like “A § B” (
entering_substack|...|leaving_substack) - from what is left, detach the group of parameters that start with “Template:” and make them look like “{{A}}” (
entering_substack|...|leaving_substack) - from what is left, detach the group of parameters that start with “Module:” and make them look like “{{#invoke:A}}” (
entering_substack|...|leaving_substack) - with what is left, create a plain wikilink
- merge the three groups
- print the result
- That's all. If you want you can experiment with the module at Module:Params/testcases/sandbox3 (or another of these sandbox pages). --Grufo (talk) 15:33, 16 October 2025 (UTC)
- Module:Pagelist is 106 lines of simple and straight-forward code that can easily be modified by many users. This template instead is 36 lines of code that is incomprehensible to almost everyone. Of course it's readable for you, because you created the proto-language in which it's written. :-)
- Of course, I'm just one editor among many. If others feel that 36 lines of module:params code is better than 106 lines of Lua code, so be it. But I'd expect that many others would agree that this approach is not sustainable, and we should use Lua modules for cases like this. — Chrisahn (talk) 15:48, 16 October 2025 (UTC)
- Although powerful, Module:Params is a specialized module. It specializes in… parameters. So it cannot check a Wikidata entry, it cannot check the position of a character within a string, and so on (although it can invoke other modules). So there will always be cases in which Lua modules are needed. Printing a list of parameters however is what Module:Params specializes in, so definitely works well for cases like this. But let's see how others will react. In the meanwhile, I invite you to play with the module's sandbox pages (it is possible to do experiments directly in the preview windows, without having to save the page). You might discover it is not that complicate after all, especially if you are already a programmer. --Grufo (talk) 15:59, 16 October 2025 (UTC)
- Regarding the technical details: Thanks for the explanation! I still don't understand what a substack is, but the step-by-step explanation sounds good. If I understand correctly, in a common imperative language like Lua the steps that start at "detach the group" and end at "merge the three groups" would be done like this:
- iterate through the parameters, and for each one...
- if it contains "#", emit "A § B"
- else, if it starts with "Template:", emit "{{A}}"
- else, if it starts with "Module:", emit "{{#invoke:A}}"
- otherwise, emit a plain wikilink
- iterate through the parameters, and for each one...
- Did I get that right? If I did, it looks like the substack is a way to implement iteration, but based on recursion, as it's often done in functional languages. (EDIT: Updated to clarify that the second and third ifs are else-ifs.)
- — Chrisahn (talk) 16:04, 16 October 2025 (UTC)
- The result is correct, but your algorithm is not the same and might be slower than what this template does, because for each parameter you do an if/else chain with four conditions. This template instead checks one condition on all the parameters (if it contains "#"), then checks the second conditions only on the parameters that are still left out, then the third condition on an even smaller group, and so on. A “substack” is a copied group of parameters that can only be merged with the real parameters or dropped (see
dropping_substack). They can also be used to remove parameters (seedetaching_substack). Once inside a substack, all the directives (except flag modifiers) apply to that substack. --Grufo (talk) 16:16, 16 October 2025 (UTC)- No, the total number of checks is the same for both approaches. The if-elseif-else chain only checks the remaining conditions if the previous ones are false. (I updated my pseudo-code above to make that clearer.) The substack approach might be slower because it makes several partial copies of the list of parameters. But anyway... It's notoriously difficult to estimate performance just by looking at code. We'd have to measure this, otherwise we're just guessing. And it doesn't matter much anyway. I don't think there would be any measurable performance difference between the two approaches, but even if one was two times faster than the other, or five times faster, it wouldn't matter. The main point here is the maintainability of the code. Performance is much less important in the cases we're talking about. — Chrisahn (talk) 16:46, 16 October 2025 (UTC)
- In both cases, the if/else chain and the “filter” approach of this template, performance depends also on the order you write your conditions (you don't want the most likely condition to be checked last), but I agree, performance is not important here, I was just explaining what this template does. The similarity with CSS comes from this “cascade” approach. --Grufo (talk) 16:57, 16 October 2025 (UTC)
- No, the total number of checks is the same for both approaches. The if-elseif-else chain only checks the remaining conditions if the previous ones are false. (I updated my pseudo-code above to make that clearer.) The substack approach might be slower because it makes several partial copies of the list of parameters. But anyway... It's notoriously difficult to estimate performance just by looking at code. We'd have to measure this, otherwise we're just guessing. And it doesn't matter much anyway. I don't think there would be any measurable performance difference between the two approaches, but even if one was two times faster than the other, or five times faster, it wouldn't matter. The main point here is the maintainability of the code. Performance is much less important in the cases we're talking about. — Chrisahn (talk) 16:46, 16 October 2025 (UTC)
- The result is correct, but your algorithm is not the same and might be slower than what this template does, because for each parameter you do an if/else chain with four conditions. This template instead checks one condition on all the parameters (if it contains "#"), then checks the second conditions only on the parameters that are still left out, then the third condition on an even smaller group, and so on. A “substack” is a copied group of parameters that can only be merged with the real parameters or dropped (see
- But Module:Pagelist is 106 lines of code. This template instead is only 36 lines. One point of Module:Params is that you can do simple things or complicate things with it. The module cannot do logic (there is nothing like if/else). The approach is closer to CSS than to a programming language. What this template does might actually be easier than it looks (regular expressions are indeed difficult, at least for the eye – but these don't depend on what tool you use). The
- Most of the examples you mentioned are one-liners, e.g.
Question regarding 'flushing'
[edit]Thanks for the explanation of the substack code you provided in the previous section! I have another technical question. The entering_substack and leaving_substack parameters (I'm tempted to call them "instructions", because the whole thing looks like a program, but I digress...) are nicely nested. Each entering_substack block contains a detaching_substack parameter. One of the blocks also contains a flushing parameter, and at the end of the code, before list_values, there are three flushing parameters. What does each occurrence of flushing mean? I guess it emits the output (result? state?) of some previous substack, but I'm not sure. — Chrisahn (talk) 18:21, 16 October 2025 (UTC)
- Hi Chrisahn. The copied parameters can be stacked on a pile by (and only by) these three directives:
leaving_substack,snapshottingandremembering. The parameters on this pile can be re-accessed later. Every time you writeflushing, the group of parameters on top of the pile will be merged with the current stack and removed from the pile (so if you writeflushinginside a substack, the merging will happen with the substack). In the case in question, we have a sub-substack inside a substack, so the flushing will be inside the first-level substack. To explain what I do, I separate the links with only a section (e.g. “#Foo” – these are in the sub-substack) from those with both a page and a section (e.g. “Foo#Bar” – these are in the substack), in order to ensure that the first group is rendered as “[[#Foo|§ Foo]]” and not as “[[#Foo| § Foo]]”. I need to merge the section-only parameters (“#Foo”) with the page+section parameters (“Foo#Bar”) only after the two groups have been dealt with separately. That is what the firstflushingdoes. --Grufo (talk) 18:34, 16 October 2025 (UTC)- @Chrisahn: Maybe an example can clarify the meaning of
flushingbetter: {{#invoke:params| setting|i|; | imposing|1|one| imposing|2|two| imposing|3|three| imposing|4|four| entering_substack|new|<!-- Create an empty substack --> imposing|2|owt| leaving_substack|<!-- Put the substack on top of the pile and return to the main stack --> snapshotting| entering_substack| discarding|4| mapping_to_uppercase| merging_substack| flushing|<!-- Flush the only substack in the pile, which contains only ‘|2=owt’ --> list_values }}
- yields
- ONE; owt; THREE; four
- --Grufo (talk) 09:52, 17 October 2025 (UTC)
- @Chrisahn: Maybe an example can clarify the meaning of