Variables and Procedures allow us to write a small chunk of wikitext in one place, give it a name that concisely describes what it is, and use it in multiple places. For the same reason that TiddlyWiki supports variables and procedures when writing complicated wikitext, it supports functions when writing complicated filters. A function is a special kind of variable that contains a filter expression.
Like a procedure, a function can have parameters. Parameters passed to a function are available as variables within the function's filter expression, just like parameters passed to a procedure are available as variables within the body of the procedure.
Unlike a procedure, a function evaluates the filter expression within it and produces output tiddlers when called, rather than rendering its contents as wikitext.
The syntax for basic function definitions and calls will be very familiar, because it's identical to that for procedures except that the definition uses the word function
instead of procedure
:
\function multiply-by-two(number) [<number>multiply[2]]
<<multiply-by-two 4>>
8
You can also use the $transclude
widget to call a function, exactly like you use it to call a procedure.
Since filters are typically only a single line, most function definitions will only need one line and so can omit the \end
. However, if you have a very complex filter, line breaks between runs are allowed and can make the function easier to read.
Using a function in a filter
Occasionally, calling a single function by itself can be useful. However, functions are typically most useful when used inside other filters to build up more complex filter pipelines that can still be easily read and understood.
Like a procedure, you can call a function within a filter pipeline using the <angle bracket>
syntax:
\function multiply-by-two(number) [<number>multiply[2]]
{{{ [<multiply-by-two 4>] }}}
You can also call a function using the function
filter operator. This operator's first parameter is the name of the function, and subsequent parameters are passed through to that function:
\function multiply-by-two(number) [<number>multiply[2]]
{{{ [function[multiply-by-two],[4]] }}}
Recall that filtering on a field is so common that you can omit the field:
part from the field:fieldname
operator and just say fieldname
, as long as the field name does not conflict with a built-in operator. You can use a similar shortcut for function names, treating the function name like a filter operator, but with one extra restriction: the function name must contain a dot (.
).
Taking advantage of this syntax can be as simple as prefixing your function names with a single .
. (Of course, the .
can go anywhere in the name, if it makes more sense somewhere else.)
\function .multiply-by-two(number) [<number>multiply[2]]
{{{ [.multiply-by-two[4]] }}}
Accessing input tiddlers
So far, our function has ignored its input tiddlers (instead injecting one of its parameters into the start of the filter pipeline), and we've only tried to call it at the start of a filter pipeline. However, functions are fully able to access their input tiddlers, just like any other filter operator. Let's change our test filter so that it operates on its input tiddlers instead:
\function .multiply-input-by-two() [multiply[2]]
{{{ [enlist[1 2 3 4].multiply-input-by-two[]] }}}
It's not possible to choose the input tiddlers of a function called with the <<variable transclusion syntax>>
. The input tiddlers will always be all tiddlers in your wiki.
Nesting functions
You can use functions inside other functions. This makes it possible to build up complicated processes or formulas while giving each part a name.
As an example, here are some carefully decomposed functions for calculating the area and circumference of a circle with a given radius. In the example, we've also used several $list
s to loop over the different circle sizes and measurements we want to use, so we only have to call each function once. (Review the conditional procedure exercise if you don't remember how this works.) In real life, the amount of decomposition of the formulas shown here is probably overkill – since the formulas are straightforward, you might even find this makes it harder to understand what's going on.
\procedure pi() 3.1415926
\function .square() [power[2]]
\function .radius-to-area() [.square[]multiply<pi>]
\function .radius-to-diameter() [multiply[2]]
\function .radius-to-circumference() [.radius-to-diameter[]multiply<pi>]
\function circle-area(radius) [<radius>.radius-to-area[]fixed[2]]
\function circle-circumference(radius) [<radius>.radius-to-circumference[]fixed[2]]
<$list filter="1 2 3" variable="r">
<li>
Circle with radius <<r>>
<ul>
<$list filter="area circumference" variable="measurement">
<$let
funcName=`circle-$(measurement)$`
>
<li>
<<measurement>>:
<$transclude $variable=<<funcName>> radius=<<r>>/>
</li>
</$let>
</$list>
</ul>
</li>
</$list>
- area: 3.14
- circumference: 6.28
- area: 12.57
- circumference: 12.57
- area: 28.27
- circumference: 18.85
Exercises
In the circle math example, we carefully ordered the functions so that if function B calls function A, function A always comes before function B. What happens if you change the order of the functions so this isn't true? Why do you think this is?
go to answerReturn to the alphabetically last description exercise:
{{{ [all[shadows]tag[$:/tags/EditorToolbar]get[description]split[{{]split[}}]get[text]!sort[]first[]] }}}
Extract one or more functions from this filter to make it easier to understand. That is, group some of the filter steps by the task they accomplish and create a function with a name that clearly describes what those steps do, and change the filter to call that function instead of using the steps directly.
go to answerThis snippet is supposed to show a list of all tiddlers in the wiki whose names start with C. Why do you think it doesn't show all the tiddlers? Fix it so it does.
\function tiddlers-starting-with-C() [all[shadows+tiddlers]prefix[C]]
<$list filter=<<tiddlers-starting-with-C>>>
<$link /><br>
</$list>
We saw two different ways to create a function that multiplies its input by two, depending on whether we wanted to take the multiplicand as an input tiddler or as a parameter:
\function multiply-by-two(number) [<number>multiply[2]]
\function multiply-input-by-two [multiply[2]]
<<multiply-by-two 4>>
{{{ [[4]function[multiply-input-by-two]] }}}
Create a multiply-anything-by-two
function that works in either of these roles. That is:
\function multiply-anything-by-two(??) ??
* {{{ [[4]function[multiply-anything-by-two]] }}} should equal 8.
* {{{ [function[multiply-anything-by-two],[4]] }}} should equal 8.
In the local calls exercise, we created template logic to display whether a contact's phone number was local. Write three functions to determine the type of a number, according to the definitions used in that exercise:
phone.toll-free[number]
phone.local[number]
phone.long-distance[number]
Each function should take a phone number (not just an area code) as its parameter, and return that parameter unmodified as its output if the area code is in that class, and yield no output if it is not.
Modify the template to use these functions.
Tip 1: In the previous answer to the exercise, we did this to check if an area code was in the list of toll-free area codes:
[enlist{TollFreeAreaCodes}match<areaCode>]
That won't work anymore because the area code will need to be coming in the pipeline as an input tiddler, rather than being in a variable. You can't simply invert them, like [<areaCode>enlist{TollFreeAreaCodes}]
, because enlist
is a constructor and will just wipe out the area code. Instead, use a filter run with the :intersection
prefix:
[...get-the-area-code-somehow] :intersection[enlist{TollFreeAreaCodes}]
:intersection
causes TiddlyWiki to evaluate both filter runs separately, then compare them and keep only the values that are in both.
Tip 2: In order to pass the parameter through unmodified without converting it to just the area code, you'll need to do most of the work in a run with the :filter
prefix. When a run has this prefix, TiddlyWiki will evaluate that filter once for each output tiddler of the preceding run, and keep only the tiddlers for which the new run returns any value. The value actually returned by the run is thrown away – it only matters whether there is one or not.
Because the pattern in Hint 1 requires two filter runs, and you can't directly nest multiple filter runs within another filter run, you'll actually need to use a function to combine those two runs, and call that function in the run with the prefixed :filter
.
Here's a complete example, for a function phone.outside-line-or-operator
, that shows the pattern you'll want to use:
\procedure outside-line-starts() [[9]] [[0]]
\function is-outside-line-or-operator() [split[]first[]] :intersection[enlist<outside-line-starts>]
\function phone.outside-line-or-operator(number) [<number>] :filter[function[is-outside-line-or-operator]]
Will dialing these numbers give you an outside line or an operator (number begins with a 9 or a 0, respectively)?
* Yes: {{{ [phone.outside-line-or-operator[92223334444]] }}}
* No: {{{ [phone.outside-line-or-operator[2223334444]] }}}
Will dialing these numbers give you an outside line or an operator (number begins with a 9 or a 0, respectively)?
- Yes: 92223334444
- No: