summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/cld/cld-moreonfunctions.tex
blob: fab22515eac08055e2efa4bb34866eb0da5f4e09 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
% language=uk

\startcomponent cld-moreonfunctions

\environment cld-environment

\startchapter[title=More on functions]

\startsection[title=Why we need them]

\index{functions}

In a previous chapter we introduced functions as arguments. At first sight this
feature looks strange but you need to keep in mind that a call to a \type
{context} function has no direct consequences. It generates \TEX\ code that is
executed after the current \LUA\ chunk ends and control is passed back to \TEX.
Take the following code:

\startbuffer
context.framed( {
    frame = "on",
    offset = "5mm",
    align = "middle"
  },
  context.input("knuth")
)
\stopbuffer

\typebuffer

We call the function \type {framed} but before the function body is executed, the
arguments get evaluated. This means that \type {input} gets processed before
\type {framed} gets done. As a result there is no second argument to \type
{framed} and no content gets passed: an error is reported. This is why we need
the indirect call:

\startbuffer
context.framed( {
    frame = "on",
    align = "middle"
  },
  function() context.input("knuth") end
)
\stopbuffer

\typebuffer

This way we get what we want:

\startlinecorrection
\ctxluabuffer
\stoplinecorrection

The function is delayed till the \type {framed} command is executed. If your
applications use such calls a lot, you can of course encapsulate this ugliness:

\starttyping
mycommands = mycommands or { }

function mycommands.framed_input(filename)
  context.framed( {
    frame = "on",
    align = "middle"
  },
  function() context.input(filename) end
end

mycommands.framed_input("knuth")
\stoptyping

Of course you can nest function calls:

\starttyping
context.placefigure(
  "caption",
  function()
    context.framed( {
      frame = "on",
      align = "middle"
    },
      function() context.input("knuth") end
    )
  end
)
\stoptyping

Or you can use a more indirect method:

\starttyping
function text()
  context.framed( {
      frame = "on",
      align = "middle"
    },
    function() context.input("knuth") end
  )
end

context.placefigure(
  "none",
  function() text() end
)
\stoptyping

You can develop your own style and libraries just like you do with regular \LUA\
code. Browsing the already written code can give you some ideas.

\stopsection

\startsection[title=How we can avoid them]

\index{delaying}
\index{nesting}

As many nested functions can obscure the code rather quickly, there is an
alternative. In the following examples we use \type {test}:

\startbuffer
\def\test#1{[#1]}
\stopbuffer

\typebuffer \getbuffer

\startbuffer
context.test("test 1 ",context("test 2a")," test 3")
\stopbuffer

\typebuffer

This gives: \ctxluabuffer. As you can see, the second argument is executed before
the encapsulating call to \type {test}. So, we should have packed it into a
function but here is an alternative:

\startbuffer
context.test("test 1 ",context.delayed("test 2a")," test 3")
\stopbuffer

\typebuffer

Now we get: \ctxluabuffer. We can also delay functions themselves,
look at this:

\startbuffer
context.test("test 1 ",context.delayed.test("test 2b")," test 3")
\stopbuffer

\typebuffer

The result is: \ctxluabuffer. This feature also conveniently permits the use of
temporary variables, as in:

\starttyping
local f = context.delayed.test("test 2c")
context("before ",f," after")
\stoptyping

Of course you can limit the amount of keystrokes even more by
creating a shortcut:

\starttyping
local delayed = context.delayed

context.test("test 1 ",delayed.test("test 2")," test 3")
context.test("test 4 ",delayed.test("test 5")," test 6")
\stoptyping

So, if you want you can produce rather readable code and readability of code is
one of the reasons why \LUA\ was chosen in the first place. This is a good
example of why coding in \TEX\ makes sense as it looks more intuitive:

\starttyping
\test{test 1 \test{test 2} test 3}
\test{test 4 \test{test 5} test 6}
\stoptyping

There is also another mechanism available. In the next example the second
argument is actually a string.

\starttyping
local nested = context.nested

context.test("test 8",nested.test("test 9"),"test 10")
\stoptyping

There is a pitfall here: a nested context command needs to be flushed explicitly,
so in the case of:

\starttyping
context.nested.test("test 9")
\stoptyping

a string is created but nothing ends up at the \TEX\ end. Flushing is up to you.
Beware: \type {nested} only works with the regular \CONTEXT\ catcode regime.

\stopsection

\startsection[title=Trial typesetting]

\index {prerolls}
\index {trial typesetting}

Some typesetting mechanisms demand a preroll. For instance, when determining the
most optimal way to analyse and therefore typeset a table, it is necessary to
typeset the content of cells first. Inside \CONTEXT\ there is a state tagged
\quote {trial typesetting} which signals other mechanisms that for instance
counters should not be incremented more than once.

Normally you don't need to worry about these issues, but when writing the code
that implements the \LUA\ interface to \CONTEXT, it definitely had to be taken
into account as we either or not can free cached (nested) functions.

You can influence this caching to some extend. If you say

\starttyping
function()
  context("whatever")
end
\stoptyping

the function will be removed from the cache when \CONTEXT\ is not in the trial
typesetting state. You can prevent removal of a function by returning \type
{true}, as in:

\starttyping
function()
  context("whatever")
  return true
end
\stoptyping

Whenever you run into a situation that you don't get the outcome that you expect,
you can consider returning \type {true}. However, keep in mind that it will take
more memory, something that only matters on big runs. You can force flushing the
whole cache by:

\starttyping
context.restart()
\stoptyping

An example of an occasion where you need to keep the function available is in
repeated content, for instance in headers and footers.

\starttyping
context.setupheadertexts {
  function()
    context.pagenumber()
    return true
  end
}
\stoptyping

Of course it is not needed when you use the following method:

\starttyping
context.pagenumber("pagenumber")
\stoptyping

Because here \CONTEXT\ itself deals with the content driven by the keyword \type
{pagenumber}.

\stopsection

\startsection[title=Steppers]

The \type {context} commands are accumulated within a \type {\ctxlua} call and
only after the call is finished, control is back at the \TEX\ end. Sometimes you
want (in your \LUA\ code) to go on and pretend that you jump out to \TEX\ for a
moment, but come back to where you left. The stepper mechanism permits this.

A not so practical but nevertheless illustrative example is the following:

\startbuffer
\startluacode
  context.stepwise (function()
    context.startitemize()
       context.startitem()
         context.step("BEFORE 1")
       context.stopitem()
       context.step("\\setbox0\\hbox{!!!!}")
       context.startitem()
         context.step("%p",tex.getbox(0).width)
       context.stopitem()
       context.startitem()
         context.step("BEFORE 2")
       context.stopitem()
       context.step("\\setbox2\\hbox{????}")
       context.startitem()
         context.step("%p",tex.getbox(2).width)
       context.startitem()
         context.step("BEFORE 3")
       context.stopitem()
       context.startitem()
         context.step("\\copy0\\copy2")
       context.stopitem()
       context.startitem()
         context.step("BEFORE 4")
         context.startitemize()
           context.stepwise (function()
             context.step("\\bgroup")
             context.step("\\setbox0\\hbox{>>>>}")
             context.startitem()
               context.step("%p",tex.getbox(0).width)
             context.stopitem()
             context.step("\\setbox2\\hbox{<<<<}")
             context.startitem()
               context.step("%p",tex.getbox(2).width)
             context.stopitem()
             context.startitem()
               context.step("\\copy0\\copy2")
             context.stopitem()
             context.startitem()
               context.step("\\copy0\\copy2")
             context.stopitem()
             context.step("\\egroup")
           end)
         context.stopitemize()
       context.stopitem()
       context.startitem()
         context.step("AFTER 1\\par")
       context.stopitem()
       context.startitem()
         context.step("\\copy0\\copy2\\par")
       context.stopitem()
       context.startitem()
         context.step("\\copy0\\copy2\\par")
       context.stopitem()
       context.startitem()
         context.step("AFTER 2\\par")
       context.stopitem()
       context.startitem()
         context.step("\\copy0\\copy2\\par")
       context.stopitem()
       context.startitem()
         context.step("\\copy0\\copy2\\par")
       context.stopitem()
     context.stopitemize()
end)
\stopluacode
\stopbuffer

\typebuffer

This gives an (ugly) itemize with a nested one:

\getbuffer

As you can see in the code, the \type {step} call accepts multiple arguments, but
when more than one argument is given the first one is treated as a formatter.

\stopsection

\stopchapter

\stopcomponent