Introducing Multi-valued Variables

Sadly I don’t think this is possible. The existing semantics are that [[nosuchvariable]getvariable[]] returns a result list of a single blank item. However, in the case of a multi-valued variable, we need to be able to represent an empty list, and so it would be natural for [[nosuchvariable]getvariable[]] to return an empty list.

Hi @jeremyruston, could enlist:varlist[name] work, with maybe a better suffix name? The first item of the parameter (per definition a title list) is taken as the name of the variable and enlisted as a multi-valued variable.

Another way to reuse enlist could be enlist:‹name›[], where ‹name› is the name of the multi-valued variable, with the boundary condition that it cannot be “dedupe” or “raw”.

I’ve made some further updates that make things a little simpler:

  • Removed the <$letlist> widget in favour of updating the existing <$let> widget so that it actually assigns the complete result list of evaluating the attribute to a multi-valued variable. This is backwards compatible because existing usages will only see a single value for the variable
  • Reversed the order of the :let filter run prefix so that the values to be assigned are taken from the input title list and the name of the variable is taken from the filter run itself

I’m pleased with both these changes, but I think there’s still a little way to go. I’d like to find a better name for the :let filter run prefix, and I’d like to figure out an alternative to the varlist operator.

These are GREAT changes, love the simplicity and that syntax is MUCH cleaner. I would love to be able to get to <myvar> within the filter rather than varlist[myvar] for some extra functionality if we can get there, but these recent changes are still big improvements. Oh, and I like the :let naming myself for what it’s worth, Thanks!

1 Like

I also like :let, but :set might be a good alternative if we need one. As I said above, I think it’d be helpful to leverage users’ knowledge of existing TW terminology, and many people already associate let and set with assigning variable names.

I’d also love to be able to use enlist<myvar> as an alternative to varlist[myvar], if that’s at all possible.

1 Like

Thanks for the feedback @stobot @etardiff.

With the latest updates, assignments like <$let a={{{ [all[tiddlers]] }}} > will now create a multi-valued variable containing the complete results instead of the usual single valued variable. We therefore need to make sure that it always behave as a single valued variable for all existing usages of variables. That means that all of those existing usages cannot be changed.

In particular, the problem with allowing <myvar> to return a result list instead of a single value is that it would mean that an assignment like the example above would no longer behave in the same way as at present: because it would now be interpreted as an assignment to a multi-valued variable, all the values would be retrieved by the <myvar> syntax instead of just the first.

One way around these issues would be to introduce a new syntax for accessing multi-valued variables as operator operands. It seems like it might be useful: it would allow us to simply pass lists to user defined functions, and introduce much simpler list operations like combine, intersect etc.

The syntax would have to be a pair of one or more characters that can be used to quote the variable name. There aren’t many open/close character pairs that we haven’t used and that are easy to type on ordinary keyboards. The only obvious one is round brackets:

[my.function(multivaluedvarname),<anothervarname>,(anothermultivaluedvarname)]

That seems inconsistent with our existing usage of <varname>. Another alternative would be to use double angle brackets:

[my.function<<multivaluedvarname>>,<anothervarname>,<<anothermultivaluedvarname>>]

Neither of these proposals is 100% backwards compatible, but it seems unlikely that anyone is using variable names that start with < and end with >, and so I wouldn’t think it would be a problem.

It might be worth mentioning a related idea that I have considered: to introduce quote symbols for using an entire sub filter as an operator operand. It’s obviously crazy because it leads to an even more unintelligible soup of similar looking symbols. For example:

[my.function{{{ [<something>addsuffix<anotherthing>] }}}]

Again, maddeningly, I don’t think we can do this with a significant break in backwards compatibility. The problem is essentially the same as above, but it can be restated in simpler terms. If myvar was assigned as <$let a={{{ [all[tiddlers]] }}} > then it would behind the scenes be a multivalued variable. The proposal here is that because the variable is multi-valued then the enlist operator would return all the values in the variable, which would not be compatible with the existing usage which would take the first result in the list and treat it as a title list.

This case could be fixed with an explicit suffix to the enlist operator: enlist:all[] perhaps, or enlist:multi[]. However, that does seem a cop out, and rather undermines the goal of introducing as little new syntax as possible.

With the second proposal above of using double angle brackets to indicate access to a multi-valued variable, it would be possible to use [<<multivar>>] within a filter to obtain the complete result list from a multi-valued variable.

Hmmm.

Personally, I’d really prefer single round brackets to double angle brackets for this usage. We spend so much time telling people not to use constructions like my.function<<multivaluedvarname>>; I imagine that confusion will only increase if new users are encountering that sort of syntax.

I’m also rather partial to enlist:all, though; as new syntax goes it’s certainly better than varlist, and looks less like an error than the double angle brackets.

5 Likes

Even more time is spent forming the habit after being told how to do it the right way. Indeed this would be confusing and what makes the matters worse is that there’s no linter that could produce verbose error messages in these situations, so yes, I’m afraid it is only going to make it harder.

2 Likes

Maybe we could have a special naming convention that, if used, treats MVVs differently, similar to functions and the period. Myself, I never use function[my-function] in a filter, but rather .my-function[]. Similarly, if we “mark” MVVs with something like a plus in their name, [<my+variable>] would be a shortcut for varlist[my+variable] – since the latter would seldom be used, its unwieldy name is of secondary concern.
Also, similar to function syntax, this might help WikiText programmers to identify which variable is an MVV and which is not. I would probably implement some kind of naming convention anyway, to preserve what’s left of my sanity, and would welcome some guidance that directly derives from core functionality.

@jeremyruston

I am with @etardiff and @vuk – I think [<<multivar>>] is confusing. Especially, since there are many existing posts here in the forum, which tell our users a different story.

I would prefer [(mulitvar)]

@Yaisog suggested a special+naming, which I think is also confusing.

For the reasons stated here, I definitely agree that the round braces would read better.

But I’m curious as to whether we would have an obvious parallel for double parentheses outside a filter.

Brackets Inside [filter run] Outside filter run
[] act.on[title] [[title]]
{} use{transclusion!!var} {{transclusion!!var}}
<> work.with<my-variable> <<my-variable>>
() use.all(multi-val) (( ??? ))

While such a parallel clearly is not essential, it would be nice to have this sort of consistency. Is there a logical candidate?

2 Likes

I’ve pushed an update to add support for single round brackets, which also allows the “varlist” operator to be removed. Once again, it seems like a big win.

Very good question. Perhaps ((my variable)) should take a cue from {{{ filter }}} and display all of the results with wikification. On the other hand, I never liked the behaviour of {{{ filter }}} and so we probably shouldn’t perpetuate it.

Maybe instead of just concatenating the results, ((variable) should wikify them with a double line break in between each item. That would generally mean that paragraphs would be generated, which would make things much more readable. It would still largely be a tool for debugging, I suspect.

1 Like

I can’t help dreaming of the possibilities offered by a full fledged syntax like:

((variable||template|parameter1|parameter2))

Fred

I’ve made a further improvement that simplifies things even more: a shortcut syntax => for the let filter run prefix:

[my.custom.function<param>,[2]] =>index [<data>jsonget[top],<index>]

Using the shortcut syntax also avoids having to use double square brackets around the name of the variable.

1 Like

While not strictly WikiText, double parentheses are used for keyboard shortcut references.

1 Like

Jeremy and all,

I hope nobody would be relying on a solution with filters that include angle bracket characters like this, but in theory someone could have backward compatibility problems if their wiki had something like this:

{{{ =<< Fake Guillemets =>> +[join[ ]]}}}

(Above does yield << Fake Guillemets >> on oficial releases up till now, but with the proposed shortcut syntax it would yield nothing, until user makes fix, as in {{{ =[[<<]] Fake Guillemets =[[>>]] +[join[ ]]}}})

Luckily the more likely occasion for => unwittingly appearing in someone’s existing wiki would involve having a space after, and this does not seem to cause a problem (since shortcut is triggered only when => is immediately followed by non-space character(s)):

{{{ =0 =< =1 =< =2 => =1 => =0 +[join[ ]]}}} still behaves as expected in new build preview, to yield
0 < 1 < 2 > 1 > 0

I have no perspective on whether reasonable backward compatibility needs to anticipate such bizarre use-cases as the sloppy Fake Guillemets. Just making note in case further discussion is warranted.

UPDATE: I realize a somewhat less outlandish case is where people are tempted to use >> as a kind of shorthand arrow, as in:

{{{ Family =>> Genus =>> Species =>> Population +[join[ ]] }}}
to yield Family >> Genus >> Species >> Population.

Again, this would be unwise usage (double-angle brackets should never be used as graphic elements by anyone familiar with TiddlyWIki!), and it would be easy to fix once flagged… Conceivably it’s out there somewhere though…

As you say, there are some edge cases that will no longer work as before. My thinking was that these cases involve the use of angle brackets, which most users would be inclined to avoid given the existing usages of angle brackets by the core.

I’m open to suggestion on this, and there is certainly a case to be made for holding this change back until a subsequent release that is planned to break or bend backwards compatibility.

Especially as an R user I like the => notation as that’s really close to the assignment operator there. While a very small thing, it’s VERY notable to me that it’s two characters, whereas all of the other (shortcut) prefixes are single characters. So, while it’s NOT new syntax, it somewhat feels like it is…