Switch Case Construct

This is an old feature request from GitHub[3]. I closed it recently! I share it here, may help for better solutions.

In Javascript, C, C++ we have a programming construct called Switch-Case

See : JS: JavaScript Switch Statement,

switch(expression) {
  case x:
    // code block
    break;
  case y:
    // code block
    break;
  default:
    // code block
}

The below codes can do equivalent operation!

Case i

This is a simple solution and simple action can be used here.

\define switch(item, list:"a b c")
<$list filter="[enlist<__list__>search<__item__>]" emptyMessage="Default">
You passed <<currentTiddler>>
</$list>
\end

<<switch a>>

<<switch b>>

Case ii

This is a more sophisticated solution as it implements the code blocks through action macros

\define a() This is the action for a.
\define b() This is action for b.
\define c() This is action for c.
\define default() This is default action.

\define switch(item, list:"a b c")
<$list filter="[enlist<__list__>search<__item__>]" emptyMessage=<<default>> variable="actMacro">
<$macrocall $name=<<actMacro>> />
</$list>
\end


<<switch a>>

<<switch b>>

<<switch d>>

The request was to add <$switch>/<$case> to simply do above operation. Decision making using switch/case is a very common in other programming languages. See [3].

Reference

[1] https://groups.google.com/d/msg/tiddlywiki/dZVMyjUUPoQ/ltpC41q3DAAJ
[2] https://groups.google.com/d/msg/tiddlywiki/dZVMyjUUPoQ/grjx3E7LDAAJ
[3] Feature request: "Switch Case Construct" · Issue #4064 · Jermolene/TiddlyWiki5 · GitHub

3 Likes

@Mohammad

What you have there is more akin to a switch with break:label, I think. Except the break doesn’t prevent more matches being found.

In C-like languages, switch-case (and select-case etc) are selection mechanisms, choosing one code path (even if more than one selection matches the selection: “fall through”).

In TW, the filter is essentially an iterator which potentially visits all possible selections, matching or not, even after a match (code-path) is found. The filter should be carefully constructed to reduce the list as much as possible

I’m also concerned for huge lists with huge numbers of possibilities.

I do wonder if TWs approach to JSON datatiddlers might be more efficient. Maybe.

Ah, I just noticed this. Precisely.

Perhaps :map or :reduce might help? I have yet to play with them so, not sure.

Sorry, that’s all a bit random – need caffeine.

I recall @TW_Tones had a structure for switch cases, posted in gg. I think this was made before any of the various filter prefixes and it was certainly before the possibility to evaluate macros in filters.

Yes, this solution belong to 2019!

The “case structure” is one of the key programing structure after the most fundamental ones in coding Sequence, Iteration, Selection. I am a fan of the case structure in COBOL using EVALUATE which continues to be the most flexible I have seen because it does not preclude any of its possible actions and contains the WHEN OTHER to capture the no case case.

<$list filter="blah" template=<<true>> emptyMessage=<<false>> ></$list>

However I may just add that filters allow something similar and the the new cascade may allow our condition tests to return filters not just values.

Using the list widget we can even use emptyMessage as an “else” which is effectively what we need to replicate a CASE structure which is in effect a nested if then else structure. However I continue to find this non-intuitive to use.

I believe we have sufficient in TiddlyWiki to make good and adaptable equivalents of the CASE structure however a simple way to represent it would be nice. Perhaps smart use of the new $let statement wrapping a macro could do it?

On GitHub (see [3]) above, Jeremy stated he is interested in having such programming constructs in the core! So, perhaps in 5.2.3 or 5.2.x we have switch-case and if-then-else constructs :wink:

Mohammad, I suppose what I am saying is the “switch case” a general answer or a specific one. If for example we allowed “nested if statements” would we provide a simple way to do “case switch” and many other things?, rather than address only the “case statement”.

From deep in my memory a Case Statement often returns one of the listed items (optional default behaviour), typically returns a value in a variable, however it is possible to write multiple conditions and return values for every different condition that is found true. Even if only one value is returned the conditions tested need to be logically related and ordered to be sure.

I will look at the use of let widget for case now and come back

Ok, So I have played with the idea of “case structures” in tiddlywiki and as I suspected it really depends on what you want to achieve, display something, return something, transclude something, deal with else cases, return only one value etc…

I can see us documenting a range of case structures without necessarily developing a separate widget, in part because it would be hard to write one that can cater for the large number of possible uses. Here is a simple example;

\define case(input:"d")
<$set name=input filter="[[$input$]uppercase[]]">
<$list filter="[<input>match[A]then[Another]]" template="true"></$list>
<$list filter="[<input>match[B]then[Best]]" template="true"></$list>
<$list filter="[<input>match[C]then[Caught]]" template="true"></$list>
<$list filter="[<input>match[D]then[Default]]" template="true"></$list>
\end

<<case "A">>

Where “true” is a tiddler that displays the current tiddler.

  • The condition ensures one and only one value is returned
  • However every test is made, with a nestable if-then-else structure this could be designed better.
  • To use the result of <<case "A">> other than to display, it must be wikified/evaluated.

I can already imagine a dozen ways to do this, the question is how do we focus on the idea of a Generic Case Statement?

2 Likes

Another example, call macro according to case;

  • With default and error message
  • Case insensitive
  • Should only call the matching macro and exit.
  • The “valid-cases” could be sorted to most likely first if there were a lot of cases.
\define valid-cases() A B C D
\define case(input:"d")
<$set name=input filter="[[$input$]uppercase[]]">
   <$list filter="[enlist<valid-cases>] +[match<input>addsuffix[-case]]" variable=case-macro emptyMessage="Invalid input! (case macro)">
       <$macrocall $name=<<case-macro>> />
   </$list>
</$set>
\end

\define A-case()
A-case
\end
\define B-case()
B-case
\end
\define C-case()
C-case
\end
\define D-case()
D-case
\end


<<case "q">>

I just found Old GG thread https://groups.google.com/g/tiddlywiki/c/wGP5yGWoKgk

2 Likes

Thank you @TW_Tones, very inspiring examples! The first example which uses template gives a lot of flexibility

2 Likes

Your welcome,

What was a use case you had?

While playing with this I saw a lot of alternative possibilities, I suspect one or more will be the most general in nature.

Observations

  • SImple case switches can be hidden away in a macro and simple return a value.
  • More complex case structures that may utilise different wikitext for different inputs will most likely spread through the body of wikitext to make use of inline wikitext when a condition is true or the emptyMessage
  • Filters contain a whole suite of if then and if then else like statements within, in fact the list template (or its content) is if/true and empty message else/false as well.
    • I think this is why the possibility of nested if then else and end-if structures have being neglected somewhat.

In conclusion if we had a way to represent;

nested if then else and end-if structures

It would be easier to represent clearly quite a few other coding or logical structures, and use them in various contexts.

Look at this example;

<$list filter="condition" template="""
If true
"""
emptyMessage="""
else
""">>

If the template parameter permitted a “literal/inline” and had a alias “then”, and emptyMessage an alias of “else” we could write this;

<$list filter="condition" then="""
Do this
"""
else="""
otherwise this
""">>

or
<$list filter="condition" then="Do this" else="otherwise this">>

Allso nest

<$list filter="condition" then="""
Do this
"""
else="""
   <$list filter="condition" then="""
      Do this now
   """
   else="""
      otherwise this default
   """>>
""">>

My thought would be a widget very similar to list ideally called <$if and closed by </$if> which reads as end-if. Change template to then, and emptyMessage to else and leave all other features intact.

To clarify, I do not think that we need new widgets for if/then/else or switch/case; as @TW_Tones has shown, the required logic can be performed with the list widget. I am interested in exploring new wikitext syntax that acts a shortcut for those widgets.

As a simple example:

\if [<currentTiddler>match[yes]]
{{Foo}}
\else
{{Bar}}
\endif

Or:

\switch [<currentTiddler>]
\case "something"
{{Foo}}
\case "else"
{{Bar}}
\endswitch
2 Likes

Hi @jeremyruston
What you have have proposed is a clear and concise syntax and very semantic!
This makes branching and decision making constructs for complex logic quite simple and easy to maintain in Tiddlywiki!

I am eager to see this in the next release!

I’m excited about these ideas but I “fear” one thing:

If the user is to define and use these constructs similar to how local macro pragmas are defined and used, then this presumably means that they must be placed in the top of the text field… which must then be called in the subsequent code, right? IMO this risks “detouring” the code and make things more difficult to read. Here is another example of this problem.

Constructs like “if-then” seem too primitive to need to be invoked - or do I misunderstand the intention here? Maybe you’re only talking about a “core implementation” of these constructs that the user would somehow use it like any system macro by just providing arguments?

Thank you for this @Mohammad and @TW_Tones . Concise, clear explanations, multiple, complete, real world examples and pointers relating the solution to well known programming constructs. Beginners and long term beginners like me salute you!

Best
Watt

If/else-if/else is a solved problem by Evan Balster’s condition plugin:

As I remember it, Evan’s plugin provides widgets for if/then/else, not the wikitext route I am proposing.

Correct. And don’t get me wrong, taken at face value, I like the pragma approach.

Can you please explain more in light of @twMat’s concerns (which I share)?

The example I gave above assumes another potential improvement: that we allow pragmas to appear anywhere in a tiddler, with only some pragmas needing to be at the top of the tiddler (notably the \rules pragma). The restriction on only using \import or \define at the start of a tiddler has always been rather unnecessary.

2 Likes