Altering parameters of function

One nice thing with procedure is that you can use the parameters widget to fiddle with parameters values. But you do not have this mechanism with functions, although you wish you had it!

So I devised a scheme. See it below:

\function .great.stuff(stuff) [<stuff>!is[blank]else[news]!match[men]else[blokes]]

\function .great(stuff)
[.great.stuff<stuff>dump[great stuff?]]
:map[[This $(stuff)$ is great $1$!]substitute<currentTiddler>]
\end .great

\function .notsogreat(stuff) [[This $(stuff)$ is great $(.great.stuff)$.]substitute[]]

\function .great(stuff) [[This $(stuff)$ is great $1$!]substitute<.great.stuff>]

<$let stuff=bug>

* .great test: {{{ [.great[problems]] }}}
* .notsogreat test: {{{ [.notsogreat[problems]] }}}
* .great.stuff test: {{{ [.great.stuff[]] }}} and {{{ [.great.stuff[men]] }}} and {{{ [.great.stuff[women]] }}}

 </$let>

whose output is

* .great test: This problems is great problems!
* .notsogreat test: This problems is great news.
* .nogreatr test: This problems is great news!
* .great.stuff test: news and blokes and women

The .great function is my simple demo function. it needs to alter its stuff parameter and use the .great.stuff function to that end. There is a naming convention here: the inner function is a sub function to alter a parameter of the outer function, so is named by suffixing the outer function name by the outer function parameter name (with a prefixed point)) and the inner function parameter name is the same as the inner function parameter name. So that you know the purpose of things and where they can be used. I did use the same parameter name, hoping it to be accessed by the inner function with the value it has in the outer function.

But this is not the case. This would be working with variables (I tested it), but for functions, these parameters values are not transmitted by the call stack. Hence .nogreat and .notsogreat do not work but .great does work’ at the cost of a complex syntax, which would be very impractical if the .great function had to rely on the :map prefix to do its real job.

I also tested with a parameterless iner function, to see if that could help. No.

\function .nogreat.stuff() [<stuff>!is[blank]else[news]!match[men]else[blokes]]

\function .nogreat(stuff) [[This $(stuff)$ is great $(.nogreat.stuff)$.]substitute[]]

<$let stuff=bug>

* .nogreat test: {{{ [.nogreat[problems]] }}}
* .nogreat test: {{{ [.nogreat[men]] }}}
* .nogreat test: {{{ [.nogreat[]] }}}
* .nogreat.stuff test: {{{ [.nogreat.stuff[]] }}}

</$let>

The output of which is:

* .nogreat test: This problems is great bug.
* .nogreat test: This men is great bug.
* .nogreat test: This is great bug.
* .nogreat.stuff test: bug

If I don’t set the stuff variable I get “news” printed instead of “bug” which indicates that the stuff parameter value of the outer function does not set the stuff variable value for the inner function.

Could my problem be solved if functions could be defined within functions? If so, would it happen to be done in a later tiddlywiki version?

@jypre looks interesting. I will have to have a look at it at length to understand.

Somewhere in the forums @saqimtiaz demonstrated recursive functions but I have lost my reference to it, that may hold the key.

For some reason using the substitute and functions is not resonating with what I have already seen, but I cant explain why.

Intriguing.

Interesting problem

An alternative might be to pass the stuff parameter directly to the inner function as an input to the filter, rather than as a parameter. You could then use a filtered subtitution placeholder in your outer function, like this:

\function .great.stuff() [!is[blank]else[news]!match[men]else[blokes]]

\procedure stuff-placeholder-string() This $(stuff)$ is great ${ [<stuff>.great.stuff[]] }$

\function .great(stuff) [<stuff-placeholder-string>substitute[]]

Annoyingly you have to define your placeholder string as a seperate static variable if you’re doing this, since you can’t have a string with square brackets as a literal filter paramater.

1 Like

I am not used at all with this placeholder mechanism. It’s good to see your piece of code doing the stuff.

There is a problem with it, though: defining variable is impossible within functions, as you noted. This means messy code with variable for internal purpose of function defined out of the function. That is not going to work in the long run.

Thanks. And yes, I agree it does end up looking a bit messy, so it would be nicer if you could just pass the variable definitions down between function calls automatically in the same way that it works within transclusions.

variables cannot be created within functions. but functions can access all known variables.

so what is lacking is that function parameters be first class variables.

First things first, this is about solving problems and understanding tiddlywiki. This is not about wrongs and rights.

I have being working through this.

  • I wrote a comprehensive response, which I held off posting as I came across issues in your examples.
  • I then wrote comprehensive example of every use of functions I could think off, to illustrate how to use them.
    • This is quite comprehensive and can share anytime, but I did not want to confuse things, give what happened next.
    • The final example was to show how to achieve what you seemed to be after in your example.
    • However I then I held off sharing until I reverse engineered your examples to understand the result you would like to see.
  • I attempted to reverse engineer your example to identify what I think you expect from your functions and I see some issues.

Lets consider this filter [<stuff>!is[blank]else[news]!match[men]else[blokes]]

  • If the variable <stuff> is not blank use it, what ever its value is
    • If it !is[blank]] then we have the result the rest of the filter is ignored.
  • (else) If the value “stuff” is blank set it to “news”
  • Then if “news” is not matching “men” ALWAYS WILL BE,
  • (else) make it “blokes” ALSO NEVER/ALWAYS WILL BE ?
    • Here I got confused

So I continue reverse engineer your example, I believe I have everything to show a solution, but I am at a loss to deal with the uncertainties in your example.

Could you spell out the desired logic in this first filter?

  • and I will give you a fully worked solution and documentation that helps you understand.

@TW_Tones Sorry for being so long to answer. And thank you for your help so far.

First, I don’t think I was claiming wrong or right. I was making a point for a feature in a what I thought was a justified way.

Now, back to the filter. Its aim is to take its input, and if it’s blank, change it to “news”. Then, whatever the case, if the input is “men”’ then change it to “blokes”. otherwise, let the input as it currently is. My demo shows it done properly with the .great.stuff function.

You say that “If it !is[blank]] then we have the result the rest of the filter is ignored.” but I dn’t think it’s true: the rest of the line is not ignored. If the input is not blank, nothing is done and the else operator will not be activated. If the input is blank, tgen there is no more any input and then the else operator will be assigning “news” as a new input.

I will have to respond in a couple of days. Walked up pigeon house mountian nsw australia today :nerd_face:

  • I was talking about what I was saying, not you. I did not want to be read as saying this is wrong or right just an approach.

On returning here after my bushwalk adventure, I see to respond, I need to get back into the details of your logical requirements before proceeding.

  • It would be helpful if they were laid out in pseudocode or a condition table with each outcome addressed.
    • perhaps we can work together to set an example?
  • This would help ensure we are all working to the same end, previously I had to reverse engineer the code to be sure what outcome you wanted.
  • You can see in my last comprehensive reply I was struggling to be sure what your desired outcomes were.

Happy to help more but can we find a way to define the desired results a little more clearly in plan language rather than tiddlywiki script?, this reduces the upfront complexity allowing more certainty in the in the crafting of filters.

  • Perhaps we can develop a way and encourage the community as a whole.

@TW_Tones This is a idea. It would surely be needed to have some kind of tool for an easy way to make it. But firt, we need to see what is needed.

So here my truth table for function

|stuff variable value|output value|h
|none or empty string|“news”|
|“men”|“blokes”|
|any other input|the same input value|
|.great.stuff output values table|c

(I think we need to be able to code a table in TW dialect)

remember that function:

\function .great.stuff(stuff) [<stuff>!is[blank]else[news]!match[men]else[blokes]]

Is that what you needed?

I think that the “cascade” filter run prefix would be a suitable tool for this purpose.

@jypre I have returned to this today, and having the table only confirmed my previous results reverse engineering, but that is so easy to read and transfer to a filter.

First I built and tested the filter;

|stuff variable value||output value|h
|none or empty string|`[<stuff>is[blank]then[news]`|“news”|
|“men”|`[<stuff>match[men]then[blokes]]`|“blokes”|
|any other input|`:else[<stuff>] `|the same input value|
|.great.stuff output values table|c


<$let stuff="">

{{{ [<stuff>is[blank]then[news]] [<stuff>match[men]then[blokes]] :else[<stuff>] }}}

</$let>

Then I move the filter I am using into the function

\function .great.stuff(stuff) [<stuff>is[blank]then[news]] [<stuff>match[men]then[blokes]] :else[<stuff>]

<$let stuff="news">

<<.great.stuff>>

</$let>

References

|stuff variable value|condition|output value|h
|none or empty string|`[<stuff>is[blank]then[news]`|“news”|
|“men”|`[<stuff>match[men]then[blokes]]`|“blokes”|
|any other input|`:else[<stuff>] `|the same input value|
|.great.stuff output values table|c

Notes;

  • Notice how the first two filters transform the input only in specific cases, when the output is blank or is equal to “men”, other values just “fall through” the filter.
    • We could include additional transformations in our filter/function
  • The last filter :else[<stuff>] If none of the earlier filters generate a result, eg “news” or “blokes” the else says just use the input (in this case stuff)
    • If the input contains “news” or “blokes” rather than “blank” or “men” they will go through this “else as well”.
  • We can call the .great.stuff function with or without the parameter, if there is no parameter given, it uses the variable stuff.

Now lets return to the OT

  • In my example I use the function to derive the parameter value after some transformations. The function is the method.
  • To me it is important to realise a function really is a filter, and although we can provide parameters to the filter/function, these are additional features we did not have previously, so your question about using the parameters widget in a filter/function is in my view a category error. For example you would not say “why cant I use the parameters widget inside a filter?”, answer because “$parameters” is a widget, and Widgets can’t be used in filters.

However if you are keen to make use of the features in the parameters widget you can use it in a number of places.

  • Inside procedures
  • Inside a custom widget
  • Inside transclusions
  • Even just directly in Wiki text/ TiddlyWiki script.

I would love to illustrate all these forms and some seriously wonderful things that can be done in each of these cases with the $parameters widget.

Would you like to restate where you may like to go from here?

@jeremyruston I don’t see what you mean here. I am just doing a wish for a feature in tiddlytalk wiki syntax.

The logic of the table is a thing. The logic of my code is another. I build it like that:

  • have a default value “news”
  • then if blank value, use replacement value “news”
  • finally, if a special case is detected (“men”), have a special value “blokes”
[<stuff>!is[blank]else[news]!match[men]else[blokes]]

VS

[<stuff>is[blank]then[news]] [<stuff>match[men]then[blokes]] :else[<stuff>]

Your code is more readable than mine. But would it not be slower? In my application, this code is called virtually all the time. Speed is important. If the difference is not huge, I shall adapt your ways.

No I don’t think this would be slow at all. It is effectivly just three string comparisons.

  • You can often change the order and the way to set a filter if you have inside knowledge about the data, however what if the data changes?
  • As indicated in Performance choosing a filter order using one of the cached filter prefixes, may have an impact, but again only in extremely high volumes.

It seems performance issues arrise more in very large volumes and when a procedure creates “objects” such as buttons and then you can use a link catcher instead.

More often than not tiddlywikis performance depends on what is displayed on the screen at the one time, especially when change is taking place, often closing the sidebar, or changing tabs, or using close others is the quickest performance improvement.

Until you hit performance problems the readability and structure makes design and trouble shooting much easier and quicker, and if you need to improve performance easier to do so. This difference is with coding it is in the minutes and hours of your time but performance is in milliseconds and seconds.

  • Sure with high volumes it is the same milliseconds and seconds many times. But a rule of thumb I have, with no basis in fact is if there are likely to be 10,000 items do I even consider designing for performance. If it is time sensitive then the 1000’s of items.

Finaly lets say you have a large list with lots of calculations, you can actually let it run, then snap shot the results, so the calculations need not occur again until you ask to refresh the snapshot. This often co-insides with what we as users need any way.

[Post script]

When we use widget filter and parameters there is a lot going on within the core we are not privy to, such as have a look with the internals plugin at the parse tree or widget tree, even innocent changes may have a performance impact that throws any attempt by us to optimise something, to result in less performance. Given this once again my approach to write for readability and logic first. Because performance is rarely an issue.

When I was first studying IT my team was to design an accounting package, I had a way to abstract accounts to allow easy handling and “roll up”, I asked my teacher if the programes performance would be impacted if we had 100’s of thousands of accounts, many which users cant see, they just make the system very vercitile. He said “not only that it could handle millions” (based on 1980’s tech), so we stopped worying about the volume and designed the solution for functionality and understandability, because we understood what we were doing we extended its features far beyond everyone else, without giving the performance any thought.

  • Needless to say our result was better and was the winning team.
1 Like

@TW_Tones Thank you for your piece of advice.

In my case, the filter is used to decide by the system which special template might be used, depending of the tiddler title. So now I know not to care too much about that and if need be, I shall have a look at the performance tools I was not aware of.

Yet, I am not so sure about what you mean with function arguments being second grade denizen. As for myself, I wish you could use them even if the function was itself an argument. But that’s completely impossible for syntactic reasons, alas. Unless we extend the filter syntax a little bit. What about that:

[prefix<.myfn>(myFnArg)]

Using parenthesis to convey the unique parameter which could be transmitted to the function used as parameter. To allow for myFnArg to be a procedure or a function things like this could be met:

[prefix<.myfn>(<myFnArgProcedure>)]

or, for function, by convention, if myFnArg is a valid function name with a unique dot (anywhere but at its very end), the call myFnArg and use its result as argument. This convention would allow for a less daunting adaptation of the current syntax.

[prefix<.myfn>(myFnArg.functionToBeCalled)]

This use case is handled by the core using the cascade filter run prefix:
https://tiddlywiki.com/#Cascade%20Filter%20Run%20Prefix

@saqimtiaz I have a tiddler tagged $:/tags/EditTemplateBodyFilter with the following code to that effect:

[all[tiddlers]prefix<.datPrefix>]
:then[.countAllParts<currentTiddler>compare:integer:gt[0]then[dat-part.template]else[dat-leaf-part.template]addprefix[$:/user/gendat/templates/]]

This code should be refactorised to use cascading, shouldn’t it?

I dont recall saying this and dont think the limitation you refer to exists at all.

  • you are not the first to either under or overestimate the functions.

Personaly I would be able to help more if you stated what you want in plain language without code especially code we know is not correct.

Although I will give this a go, your illustration to a working example

[prefix<.myfn>(myFnArg)]

  • One issue I see here is you are trying to do two things at once here, retrieve the “title” and make it the prefix.
    • Should this be add prefix or are you trying to list tiddlers with the prefix?
  • Surely you want the myFnArg to use a variable or field value?

This is how you can pass arguments to a function;

  • [.myfn[myFnArg]]
  • [function[.myfn],[myFnArg]]
  • <<.myfn "p1value" "p2value">>
  • <<.myfn p1:"p1value" p2":p2value">>
  • [[value].myfn[]] the value is passed into the function like the current title (not completely)
<$let myval=something>

<<.myfn>> <!-- where you function references `<myval>` so not need to give it as a parameter.

Arggh,

  • There are too many branches to follow trying to second guess the result you want and the misconceptions you may (or may not) have.

More info in plain language please!