cld-moreonfunctions.tex /size: 9390 b    last modification: 2021-10-28 13:50
1% language=us runpath=texruns:manuals/cld
2
3\startcomponent cld-moreonfunctions
4
5\environment cld-environment
6
7\startchapter[title=More on functions]
8
9\startsection[title=Why we need them]
10
11\index{functions}
12
13In a previous chapter we introduced functions as arguments. At first sight this
14feature looks strange but you need to keep in mind that a call to a \type
15{context} function has no direct consequences. It generates \TEX\ code that is
16executed after the current \LUA\ chunk ends and control is passed back to \TEX.
17Take the following code:
18
19\startbuffer
20context.framed( {
21    frame = "on",
22    offset = "5mm",
23    align = "middle"
24  },
25  context.input("knuth")
26)
27\stopbuffer
28
29\typebuffer
30
31We call the function \type {framed} but before the function body is executed, the
32arguments get evaluated. This means that \type {input} gets processed before
33\type {framed} gets done. As a result there is no second argument to \type
34{framed} and no content gets passed: an error is reported. This is why we need
35the indirect call:
36
37\startbuffer
38context.framed( {
39    frame = "on",
40    align = "middle"
41  },
42  function() context.input("knuth") end
43)
44\stopbuffer
45
46\typebuffer
47
48This way we get what we want:
49
50\startlinecorrection
51\ctxluabuffer
52\stoplinecorrection
53
54The function is delayed till the \type {framed} command is executed. If your
55applications use such calls a lot, you can of course encapsulate this ugliness:
56
57\starttyping
58mycommands = mycommands or { }
59
60function mycommands.framed_input(filename)
61  context.framed( {
62    frame = "on",
63    align = "middle"
64  },
65  function() context.input(filename) end
66end
67
68mycommands.framed_input("knuth")
69\stoptyping
70
71Of course you can nest function calls:
72
73\starttyping
74context.placefigure(
75  "caption",
76  function()
77    context.framed( {
78      frame = "on",
79      align = "middle"
80    },
81      function() context.input("knuth") end
82    )
83  end
84)
85\stoptyping
86
87Or you can use a more indirect method:
88
89\starttyping
90function text()
91  context.framed( {
92      frame = "on",
93      align = "middle"
94    },
95    function() context.input("knuth") end
96  )
97end
98
99context.placefigure(
100  "none",
101  function() text() end
102)
103\stoptyping
104
105You can develop your own style and libraries just like you do with regular \LUA\
106code. Browsing the already written code can give you some ideas.
107
108\stopsection
109
110\startsection[title=How we can avoid them]
111
112\index{delaying}
113\index{nesting}
114
115As many nested functions can obscure the code rather quickly, there is an
116alternative. In the following examples we use \type {test}:
117
118\startbuffer
119\def\test#1{[#1]}
120\stopbuffer
121
122\typebuffer \getbuffer
123
124\startbuffer
125context.test("test 1 ",context("test 2a")," test 3")
126\stopbuffer
127
128\typebuffer
129
130This gives: \ctxluabuffer. As you can see, the second argument is executed before
131the encapsulating call to \type {test}. So, we should have packed it into a
132function but here is an alternative:
133
134\startbuffer
135context.test("test 1 ",context.delayed("test 2a")," test 3")
136\stopbuffer
137
138\typebuffer
139
140Now we get: \ctxluabuffer. We can also delay functions themselves,
141look at this:
142
143\startbuffer
144context.test("test 1 ",context.delayed.test("test 2b")," test 3")
145\stopbuffer
146
147\typebuffer
148
149The result is: \ctxluabuffer. This feature also conveniently permits the use of
150temporary variables, as in:
151
152\starttyping
153local f = context.delayed.test("test 2c")
154context("before ",f," after")
155\stoptyping
156
157Of course you can limit the amount of keystrokes even more by
158creating a shortcut:
159
160\starttyping
161local delayed = context.delayed
162
163context.test("test 1 ",delayed.test("test 2")," test 3")
164context.test("test 4 ",delayed.test("test 5")," test 6")
165\stoptyping
166
167So, if you want you can produce rather readable code and readability of code is
168one of the reasons why \LUA\ was chosen in the first place. This is a good
169example of why coding in \TEX\ makes sense as it looks more intuitive:
170
171\starttyping
172\test{test 1 \test{test 2} test 3}
173\test{test 4 \test{test 5} test 6}
174\stoptyping
175
176The \type {context.nested} variant is now an alias to \type {context.delayed} and
177no longer builds a string representation.
178
179% There is also another mechanism available. In the next example the second
180% argument is actually a string.
181%
182% \starttyping
183% local nested = context.nested
184%
185% context.test("test 8",nested.test("test 9"),"test 10")
186% \stoptyping
187%
188% There is a pitfall here: a nested context command needs to be flushed explicitly,
189% so in the case of:
190%
191% \starttyping
192% context.nested.test("test 9")
193% \stoptyping
194%
195% a string is created but nothing ends up at the \TEX\ end. Flushing is up to you.
196% Beware: \type {nested} only works with the regular \CONTEXT\ catcode regime.
197
198\stopsection
199
200\startsection[title=Trial typesetting]
201
202\index {prerolls}
203\index {trial typesetting}
204
205Some typesetting mechanisms demand a preroll. For instance, when determining the
206most optimal way to analyse and therefore typeset a table, it is necessary to
207typeset the content of cells first. Inside \CONTEXT\ there is a state tagged
208\quote {trial typesetting} which signals other mechanisms that for instance
209counters should not be incremented more than once.
210
211Normally you don't need to worry about these issues, but when writing the code
212that implements the \LUA\ interface to \CONTEXT, it definitely had to be taken
213into account as we either or not can free cached (nested) functions.
214
215You can influence this caching to some extend. If you say
216
217\starttyping
218function()
219  context("whatever")
220end
221\stoptyping
222
223the function will be removed from the cache when \CONTEXT\ is not in the trial
224typesetting state. You can prevent removal of a function by returning \type
225{true}, as in:
226
227\starttyping
228function()
229  context("whatever")
230  return true
231end
232\stoptyping
233
234Whenever you run into a situation that you don't get the outcome that you expect,
235you can consider returning \type {true}. However, keep in mind that it will take
236more memory, something that only matters on big runs. You can force flushing the
237whole cache by:
238
239\starttyping
240context.restart()
241\stoptyping
242
243An example of an occasion where you need to keep the function available is in
244repeated content, for instance in headers and footers.
245
246\starttyping
247context.setupheadertexts {
248  function()
249    context.pagenumber()
250    return true
251  end
252}
253\stoptyping
254
255Of course it is not needed when you use the following method:
256
257\starttyping
258context.pagenumber("pagenumber")
259\stoptyping
260
261Because here \CONTEXT\ itself deals with the content driven by the keyword \type
262{pagenumber}.
263
264\stopsection
265
266\startsection[title=Steppers]
267
268The \type {context} commands are accumulated within a \type {\ctxlua} call and
269only after the call is finished, control is back at the \TEX\ end. Sometimes you
270want (in your \LUA\ code) to go on and pretend that you jump out to \TEX\ for a
271moment, but come back to where you left. The stepper mechanism permits this.
272
273A not so practical but nevertheless illustrative example is the following:
274
275\startbuffer
276\startluacode
277  context.stepwise (function()
278    context.startitemize()
279       context.startitem()
280         context.step("BEFORE 1")
281       context.stopitem()
282       context.step("\\setbox0\\hbox{!!!!}")
283       context.startitem()
284         context.step("%p",tex.getbox(0).width)
285       context.stopitem()
286       context.startitem()
287         context.step("BEFORE 2")
288       context.stopitem()
289       context.step("\\setbox2\\hbox{????}")
290       context.startitem()
291         context.step("%p",tex.getbox(2).width)
292       context.startitem()
293         context.step("BEFORE 3")
294       context.stopitem()
295       context.startitem()
296         context.step("\\copy0\\copy2")
297       context.stopitem()
298       context.startitem()
299         context.step("BEFORE 4")
300         context.startitemize()
301           context.stepwise (function()
302             context.step("\\bgroup")
303             context.step("\\setbox0\\hbox{>>>>}")
304             context.startitem()
305               context.step("%p",tex.getbox(0).width)
306             context.stopitem()
307             context.step("\\setbox2\\hbox{<<<<}")
308             context.startitem()
309               context.step("%p",tex.getbox(2).width)
310             context.stopitem()
311             context.startitem()
312               context.step("\\copy0\\copy2")
313             context.stopitem()
314             context.startitem()
315               context.step("\\copy0\\copy2")
316             context.stopitem()
317             context.step("\\egroup")
318           end)
319         context.stopitemize()
320       context.stopitem()
321       context.startitem()
322         context.step("AFTER 1\\par")
323       context.stopitem()
324       context.startitem()
325         context.step("\\copy0\\copy2\\par")
326       context.stopitem()
327       context.startitem()
328         context.step("\\copy0\\copy2\\par")
329       context.stopitem()
330       context.startitem()
331         context.step("AFTER 2\\par")
332       context.stopitem()
333       context.startitem()
334         context.step("\\copy0\\copy2\\par")
335       context.stopitem()
336       context.startitem()
337         context.step("\\copy0\\copy2\\par")
338       context.stopitem()
339     context.stopitemize()
340end)
341\stopluacode
342\stopbuffer
343
344\typebuffer
345
346This gives an (ugly) itemize with a nested one:
347
348\getbuffer
349
350As you can see in the code, the \type {step} call accepts multiple arguments, but
351when more than one argument is given the first one is treated as a formatter.
352
353\stopsection
354
355\stopchapter
356
357\stopcomponent
358