summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/about/about-metafun.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/context/sources/general/manuals/about/about-metafun.tex')
-rw-r--r--doc/context/sources/general/manuals/about/about-metafun.tex834
1 files changed, 834 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/about/about-metafun.tex b/doc/context/sources/general/manuals/about/about-metafun.tex
new file mode 100644
index 000000000..8daff05a7
--- /dev/null
+++ b/doc/context/sources/general/manuals/about/about-metafun.tex
@@ -0,0 +1,834 @@
+% language=uk
+
+\startcomponent about-metafun
+
+\environment about-environment
+
+\startchapter[title={\LUA\ in \METAPOST}]
+
+% Hans Hagen, PRAGMA ADE, April 2014
+
+\startsection[title=Introduction]
+
+Already for some years I have been wondering how it would be if we could escape
+to \LUA\ inside \METAPOST, or in practice, in \MPLIB\ in \LUATEX. The idea is
+simple: embed \LUA\ code in a \METAPOST\ file that gets run as soon as it's seen.
+In case you wonder why \LUA\ code makes sense, imagine generating graphics using
+external data. The capabilities of \LUA\ to deal with that is more flexible and
+advanced than in \METAPOST. Of course we could generate a \METAPOST\ definition
+of a graphic from data but it often makes more sense to do the reverse. I finally
+found time and reason to look into this and in the following sections I will
+describe how it's done.
+
+\stopsection
+
+\startsection[title=The basics]
+
+The approach is comparable to \LUATEX's \type {\directlua}. That primitive can be
+used to execute \LUA\ code and in combination with \type {tex.print} we can pipe
+strings back into the \TEX\ input stream. A complication is that we have to be
+able to operate under different so called catcode regimes: the meaning of
+characters can differ per regime. We also have to deal with line endings in
+special ways as they relate to paragraphs and such. In \METAPOST\ we don't have
+that complication so getting back input into the \METAPOST\ input, we can do so
+with simple strings. For that a mechanism similar to \type {scantokens} can be
+used. That way we can return anything (including nothing) as long as \METAPOST\
+can interpret it and as long as it fulfils the expectations.
+
+\starttyping
+numeric n ; n := scantokens("123.456") ;
+\stoptyping
+
+A script is run as follows:
+
+\starttyping
+numeric n ; n := runscript("return '123.456'") ;
+\stoptyping
+
+This primitive doesn't have the word \type {lua} in its name so in principle any
+wrapper around the library can use it as a hook. In the case of \LUATEX\ the
+script language is of course \LUA. At the \METAPOST\ end we only expect a string.
+How that string is constructed is completely up to the \LUA\ script. In fact, the
+user is completely free to implement the runner any way she or he wants, like:
+
+\starttyping
+local function scriptrunner(code)
+ local f = loadstring(code)
+ if f then
+ return tostring(f())
+ else
+ return ""
+ end
+end
+\stoptyping
+
+This is hooked into an instance as follows:
+
+\starttyping
+local m = mplib.new {
+ ...
+ run_script = scriptrunner,
+ ...
+}
+\stoptyping
+
+Now, beware, this is not the \CONTEXT\ way. We provide print functions and other
+helpers, which we will explain in the next section.
+
+\stopsection
+
+\startsection[title=Helpers]
+
+After I got this feature up and running I played a bit with possible interfaces
+at the \CONTEXT\ (read: \METAFUN) end and ended up with a bit more advanced runner
+where no return value is used. The runner is wrapped in the \type {lua} macro.
+
+\startbuffer
+numeric n ; n := lua("mp.print(12.34567)") ;
+draw textext(n) xsized 4cm withcolor maincolor ;
+\stopbuffer
+
+\typebuffer
+
+This renders as:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+In case you wonder how efficient calling \LUA\ is, don't worry: it's fast enough,
+especially if you consider suboptimal \LUA\ code and the fact that we switch
+between machineries.
+
+\startbuffer
+draw image (
+ lua("statistics.starttiming()") ;
+ for i=1 upto 10000 : draw
+ lua("mp.pair(math.random(-200,200),math.random(-50,50))") ;
+ endfor ;
+ setbounds currentpicture to fullsquare xyscaled (400,100) ;
+ lua("statistics.stoptiming()") ;
+ draw textext(lua("mp.print(statistics.elapsedtime())"))
+ ysized 50 ;
+) withcolor maincolor withpen pencircle scaled 1 ;
+\stopbuffer
+
+\typebuffer
+
+Here the line:
+
+\starttyping
+draw lua("mp.pair(math.random(-200,200),math.random(-50,50))") ;
+\stoptyping
+
+effectively becomes (for instance):
+
+\starttyping
+draw scantokens "(25,40)" ;
+\stoptyping
+
+which in turn becomes:
+
+\starttyping
+draw scantokens (25,40) ;
+\stoptyping
+
+The same happens with this:
+
+\starttyping
+draw textext(lua("mp.print(statistics.elapsedtime())")) ...
+\stoptyping
+
+This becomes for instance:
+
+\starttyping
+draw textext(scantokens "1.23") ...
+\stoptyping
+
+and therefore:
+
+\starttyping
+draw textext(1.23) ...
+\stoptyping
+
+We can use \type {mp.print} here because the \type {textext} macro can deal with
+numbers. The following also works:
+
+\starttyping
+draw textext(lua("mp.quoted(statistics.elapsedtime())")) ...
+\stoptyping
+
+Now we get (in \METAPOST\ speak):
+
+\starttyping
+draw textext(scantokens (ditto & "1.23" & ditto) ...
+\stoptyping
+
+Here \type {ditto} represents the double quotes that mark a string. Of course,
+because we pass the strings directly to \type {scantokens}, there are no outer
+quotes at all, but this is how it can be simulated. In the end we have:
+
+\starttyping
+draw textext("1.23") ...
+\stoptyping
+
+What print variant you use, \type {mp.print} or \type {mp.quoted}, depends on
+what the expected code is: an assignment to a numeric can best be a number or an
+expression resulting in a number.
+
+This graphic becomes:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+The runtime on my current machine is some 0.25 seconds without and 0.12 seconds
+with caching. But to be honest, speed is not really a concern here as the amount
+of complex \METAPOST\ graphics can be neglected compared to extensive node list
+manipulation. Generating the graphic with \LUAJITTEX\ takes 15\% less time.
+\footnote {Processing a small 8 page document like this takes about one second,
+which includes loading a bunch of fonts.}
+
+\startbuffer
+numeric n ; n := lua("mp.print(1) mp.print('+') mp.print(2)") ;
+draw textext(n) xsized 1cm withcolor maincolor ;
+\stopbuffer
+
+The three print command accumulate their arguments:
+
+\typebuffer
+
+As expected we get:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+\startbuffer
+numeric n ; n := lua("mp.print(1,'+',2)") ;
+draw textext(n) xsized 1cm withcolor maincolor ;
+\stopbuffer
+
+Equally valid is:
+
+\typebuffer
+
+This gives the same result:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+Of course all kind of action can happen between the prints. It is also legal to
+have nothing returned as could be seen in the 10.000 dot example: there the timer
+related code returns nothing, so effectively we have \type {scantokens("")}.
+Another helper is \type {mp.quoted}, as in:
+
+\startbuffer
+draw
+ textext(lua("mp.quoted('@0.3f'," & decimal n & ")"))
+ withcolor maincolor ;
+\stopbuffer
+
+\typebuffer
+
+This typesets \processMPbuffer. Note the \type {@}. When no percent character is
+found in the format specifier, we assume that an \type {@} is used instead.
+
+\startbuffer
+\startluacode
+table.save("demo-data.lua",
+ {
+ { 1, 2 }, { 2, 4 }, { 3, 3 }, { 4, 2 },
+ { 5, 2 }, { 6, 3 }, { 7, 4 }, { 8, 1 },
+ }
+)
+\stopluacode
+\stopbuffer
+
+But, the real benefit of embedded \LUA\ is when we deal with data that is stored
+at the \LUA\ end. First we define a small dataset:
+
+\typebuffer
+
+\getbuffer
+
+There are several ways to deal with this table. I will show clumsy as well as
+better looking ways.
+
+\startbuffer
+lua("MP = { } MP.data = table.load('demo-data.lua')") ;
+numeric n ;
+lua("mp.print('n := ',\#MP.data)") ;
+for i=1 upto n :
+ drawdot
+ lua("mp.pair(MP.data[" & decimal i & "])") scaled cm
+ withpen pencircle scaled 2mm
+ withcolor maincolor ;
+endfor ;
+\stopbuffer
+
+\typebuffer
+
+Here we load a \LUA\ table and assign the size to a \METAPOST\ numeric. Next we
+loop over the table entries and draw the coordinates.
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+We will stepwise improve this code. In the previous examples we omitted wrapper
+code but here we show it:
+
+\startbuffer
+\startluacode
+ MP.data = table.load('demo-data.lua')
+ function MP.n()
+ mp.print(#MP.data)
+ end
+ function MP.dot(i)
+ mp.pair(MP.data[i])
+ end
+\stopluacode
+
+\startMPcode
+ numeric n ; n := lua("MP.n()") ;
+ for i=1 upto n :
+ drawdot
+ lua("MP.dot(" & decimal i & ")") scaled cm
+ withpen pencircle scaled 2mm
+ withcolor maincolor ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+So, we create a few helpers in the \type {MP} table. This table is predefined so
+normally you don't need to define it. You may however decide to wipe it clean.
+
+\startlinecorrection[blank]
+\getbuffer
+\stoplinecorrection
+
+You can decide to hide the data:
+
+\startbuffer
+\startluacode
+ local data = { }
+ function MP.load(name)
+ data = table.load(name)
+ end
+ function MP.n()
+ mp.print(#data)
+ end
+ function MP.dot(i)
+ mp.pair(data[i])
+ end
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+It is possible to use less \LUA, for instance in:
+
+\startbuffer
+\startluacode
+ local data = { }
+ function MP.loaded(name)
+ data = table.load(name)
+ mp.print(#data)
+ end
+ function MP.dot(i)
+ mp.pair(data[i])
+ end
+\stopluacode
+
+\startMPcode
+ for i=1 upto lua("MP.loaded('demo-data.lua')") :
+ drawdot
+ lua("MP.dot(",i,")") scaled cm
+ withpen pencircle scaled 4mm
+ withcolor maincolor ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+Here we also omit the \type {decimal} because the \type {lua} macro is clever
+enough to recognize it as a number.
+
+\startlinecorrection[blank]
+\getbuffer
+\stoplinecorrection
+
+By using some \METAPOST\ magic we can even go a step further in readability:
+
+\startbuffer
+\startMPcode{doublefun}
+ lua.MP.load("demo-data.lua") ;
+
+ for i=1 upto lua.MP.n() :
+ drawdot
+ lua.MP.dot(i) scaled cm
+ withpen pencircle scaled 4mm
+ withcolor maincolor ;
+ endfor ;
+
+ for i=1 upto MP.n() :
+ drawdot
+ MP.dot(i) scaled cm
+ withpen pencircle scaled 2mm
+ withcolor white ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+Here we demonstrate that it also works well in \type {double} mode, which makes
+much sense when processing data from other sources. Note how we omit the
+type {lua.} prefix: the \type {MP} macro will deal with that.
+
+\startlinecorrection[blank]
+\getbuffer
+\stoplinecorrection
+
+So in the end we can simplify the code that we started with to:
+
+\starttyping
+\startMPcode{doublefun}
+ for i=1 upto MP.loaded("demo-data.lua") :
+ drawdot
+ MP.dot(i) scaled cm
+ withpen pencircle scaled 2mm
+ withcolor maincolor ;
+ endfor ;
+\stopMPcode
+\stoptyping
+
+\stopsection
+
+\startsection[title=Access to variables]
+
+The question with such mechanisms is always: how far should we go. Although
+\METAPOST\ is a macro language, it has properties of procedural languages. It also
+has more introspective features at the user end. For instance, one can loop over
+the resulting picture and manipulate it. This means that we don't need full
+access to \METAPOST\ internals. However, it makes sense to provide access to
+basic variables: \type {numeric}, \type {string}, and \type {boolean}.
+
+\startbuffer
+draw textext(lua("mp.quoted('@0.15f',mp.get.numeric('pi')-math.pi)"))
+ ysized 1cm
+ withcolor maincolor ;
+\stopbuffer
+
+\typebuffer
+
+In double mode you will get zero printed but in scaled mode we definitely get a
+different results:
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+\startbuffer
+boolean b ; b := true ;
+draw textext(lua("mp.quoted(mp.get.boolean('b') and 'yes' or 'no')"))
+ ysized 1cm
+ withcolor maincolor ;
+\stopbuffer
+
+In the next example we use \type {mp.quoted} to make sure that indeed we pass a
+string. The \type {textext} macro can deal with numbers, but an unquoted \type
+{yes} or \type {no} is asking for problems.
+
+\typebuffer
+
+Especially when more text is involved it makes sense to predefine a helper in
+the \type {MP} namespace, if only because \METAPOST\ (currently) doesn't like
+newlines in the middle of a string, so a \type {lua} call has to be on one line.
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+Here is an example where \LUA\ does something that would be close to impossible,
+especially if more complex text is involved.
+
+% \enabletrackers[metapost.lua]
+
+\startbuffer
+string s ; s := "ΤΕΧ" ; % "τεχ"
+draw textext(lua("mp.quoted(characters.lower(mp.get.string('s')))"))
+ ysized 1cm
+ withcolor maincolor ;
+\stopbuffer
+
+\typebuffer
+
+As you can see here, the whole repertoire of helper functions can be used in
+a \METAFUN\ definition.
+
+\startlinecorrection[blank]
+\processMPbuffer
+\stoplinecorrection
+
+\stopsection
+
+\startsection[title=The library]
+
+In \CONTEXT\ we have a dedicated runner, but for the record we mention the
+low level constructor:
+
+\starttyping
+local m = mplib.new {
+ ...
+ script_runner = function(s) return loadstring(s)() end,
+ script_error = function(s) print(s) end,
+ ...,
+}
+\stoptyping
+
+An instance (in this case \type {m}) has a few extra methods. Instead you can use
+the helpers in the library.
+
+\starttabulate[|l|l|]
+\HL
+\NC \type {m:get_numeric(name)} \NC returns a numeric (double) \NC \NR
+\NC \type {m:get_boolean(name)} \NC returns a boolean (\type {true} or \type {false}) \NC \NR
+\NC \type {m:get_string (name)} \NC returns a string \NC \NR
+\HL
+\NC \type {mplib.get_numeric(m,name)} \NC returns a numeric (double) \NC \NR
+\NC \type {mplib.get_boolean(m,name)} \NC returns a boolean (\type {true} or \type {false}) \NC \NR
+\NC \type {mplib.get_string (m,name)} \NC returns a string \NC \NR
+\HL
+\stoptabulate
+
+In \CONTEXT\ the instances are hidden and wrapped in high level macros, so there
+you cannot use these commands.
+
+\stopsection
+
+\startsection[title=\CONTEXT\ helpers]
+
+The \type {mp} namespace provides the following helpers:
+
+\starttabulate[|l|l|]
+\HL
+\NC \type {print(...)} \NC returns one or more values \NC \NR
+\NC \type {pair(x,y)}
+ \type {pair(t)} \NC returns a proper pair \NC \NR
+\NC \type {triplet(x,y,z)}
+ \type {triplet(t)} \NC returns an \RGB\ color \NC \NR
+\NC \type {quadruple(w,x,y,z)}
+ \type {quadruple(t)} \NC returns an \CMYK\ color \NC \NR
+\NC \type {format(fmt,...)} \NC returns a formatted string \NC \NR
+\NC \type {quoted(fmt,...)}
+ \type {quoted(s)} \NC returns a (formatted) quoted string \NC \NR
+\NC \type {path(t[,connect][,close])} \NC returns a connected (closed) path \NC \NR
+\HL
+\stoptabulate
+
+The \type {mp.get} namespace provides the following helpers:
+
+\starttabulate[|l|l|]
+\NC \type {numeric(name)} \NC gets a numeric from \METAPOST \NC \NR
+\NC \type {boolean(name)} \NC gets a boolean from \METAPOST \NC \NR
+\NC \type {string(name)} \NC gets a string from \METAPOST \NC \NR
+\HL
+\stoptabulate
+
+\stopsection
+
+\startsection[title=Paths]
+
+% {\em This section will move to the metafun manual.} \blank
+
+In the meantime we got several questions on the \CONTEXT\ mailing list about turning
+coordinates into paths. Now imagine that we have this dataset:
+
+\startbuffer[dataset]
+10 20 20 20 -- sample 1
+30 40 40 60
+50 10
+
+10 10 20 30 % sample 2
+30 50 40 50
+50 20
+
+10 20 20 10 # sample 3
+30 40 40 20
+50 10
+\stopbuffer
+
+\typebuffer[dataset]
+
+In this case I have put the data in a buffer, so that it can be shown
+here, as well as used in a demo. Look how we can add comments. The
+following code converts this into a table with three subtables.
+
+\startbuffer
+\startluacode
+ MP.myset = mp.dataset(buffers.getcontent("dataset"))
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+We use the \type {MP} (user) namespace to store the table. Next we turn
+these subtables into paths:
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.print(mp.n(MP.myset))") :
+ draw
+ lua("mp.path(MP.myset[" & decimal i & "])")
+ xysized (HSize,10ExHeight)
+ withpen pencircle scaled .25ExHeight
+ withcolor basiccolors[i]/2 ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+This gives:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+Instead we can fill the path, in which case we will also need to close it. The
+\type {true} argument deals with that:
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.print(mp.n(MP.myset))") :
+ path p ; p :=
+ lua("mp.path(MP.myset[" & decimal i & "],true)")
+ xysized (HSize,10ExHeight) ;
+ fill p
+ withcolor basiccolors[i]/2
+ withtransparency (1,.5) ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+We get:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.print(mp.n(MP.myset))") :
+ path p ; p :=
+ lua("mp.path(MP.myset[" & decimal i & "])")
+ xysized (HSize,10ExHeight) ;
+ p :=
+ (xpart llcorner boundingbox p,0) --
+ p --
+ (xpart lrcorner boundingbox p,0) --
+ cycle ;
+ fill p
+ withcolor basiccolors[i]/2
+ withtransparency (1,.25) ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+The following makes more sense:
+
+\typebuffer
+
+So this gives:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+This (area) fill is so common, that we have a helper for it:
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua("mp.size(MP.myset)") :
+ fill area
+ lua("mp.path(MP.myset[" & decimal i & "])")
+ xysized (HSize,5ExHeight)
+ withcolor basiccolors[i]/2
+ withtransparency (2,.25) ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+So this gives:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+This snippet of \METAPOST\ code still looks kind of horrible, so how can we make
+it look better? Here is an attempt. First we define a bit more \LUA:
+
+\startbuffer
+\startluacode
+local data = mp.dataset(buffers.getcontent("dataset"))
+
+MP.dataset = {
+ Line = function(n) mp.path(data[n]) end,
+ Size = function() mp.size(data) end,
+}
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+\startbuffer
+\startMPcode
+ for i=1 upto lua.MP.dataset.Size() :
+ path p ; p :=
+ lua.MP.dataset.Line(i)
+ xysized (HSize,20ExHeight) ;
+ draw
+ p
+ withpen pencircle scaled .25ExHeight
+ withcolor basiccolors[i]/2 ;
+ drawpoints
+ p
+ withpen pencircle scaled ExHeight
+ withcolor .5white ;
+ endfor ;
+\stopMPcode
+\stopbuffer
+
+We can now make the \METAPOST\ look more natural. Of course, this is possible
+because in \METAFUN\ the \type {lua} macro does some extra work.
+
+\typebuffer
+
+As expected, we get the desired result:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+Once we start making things look nicer and more convenient, we quickly end up
+with helpers like those in the next example. First we save some demo data in
+files:
+
+\startbuffer
+\startluacode
+ io.savedata("foo.tmp","10 20 20 20 30 40 40 60 50 10")
+ io.savedata("bar.tmp","10 10 20 30 30 50 40 50 50 20")
+\stopluacode
+\stopbuffer
+
+\typebuffer \getbuffer
+
+We load the data in datasets:
+
+\startbuffer
+\startMPcode
+ lua.mp.datasets.load("foo","foo.tmp") ;
+ lua.mp.datasets.load("bar","bar.tmp") ;
+ fill area
+ lua.mp.datasets.foo.Line()
+ xysized (HSize/2-EmWidth,10ExHeight)
+ withpen pencircle scaled .25ExHeight
+ withcolor green/2 ;
+ fill area
+ lua.mp.datasets.bar.Line()
+ xysized (HSize/2-EmWidth,10ExHeight)
+ shifted (HSize/2+EmWidth,0)
+ withpen pencircle scaled .25ExHeight
+ withcolor red/2 ;
+\stopMPcode
+\stopbuffer
+
+\typebuffer
+
+Because the datasets are stored by name, we can use them without worrying about
+them being forgotten:
+
+\startlinecorrection[blank] \getbuffer \stoplinecorrection
+
+If no tag is given, the filename (without suffix) is used as a tag, so the
+following is valid:
+
+\starttyping
+\startMPcode
+ lua.mp.datasets.load("foo.tmp") ;
+ lua.mp.datasets.load("bar.tmp") ;
+\stopMPcode
+\stoptyping
+
+The following methods are defined for a dataset:
+
+\starttabulate[|l|pl|]
+\HL
+\NC \type {method} \NC usage \NC \NR
+\HL
+\NC \type {Size} \NC the number of subsets in a dataset \NC \NR
+\NC \type {Line} \NC the joined pairs in a dataset making a non|-|closed path \NC \NR
+\NC \type {Data} \NC the table containing the data (in subsets, so there is always at least one subset) \NC \NR
+\HL
+\stoptabulate
+
+{\em Due to limitations in \METAPOST\ suffix handling the methods start with an
+uppercase character.}
+
+\stopsection
+
+\startsection[title=Remark]
+
+The features described here are currently still experimental but the interface
+will not change. There might be a few more accessors and for sure more \LUA\
+helpers will be provided. As usual I need some time to play with it before I make
+up my mind. It is also possible to optimize the \METAPOST||\LUA\ script call a
+bit, but I might do that later.
+
+When we played with this interface we ran into problems with loop variables
+and macro arguments. These are internally kind of anonymous. Take this:
+
+\starttyping
+for i=1 upto 100 : draw(i,i) endfor ;
+\stoptyping
+
+The \type {i} is not really a variable with name \type {i} but becomes an object
+(capsule) when the condition is scanned, and a reference to that object when the
+body is scanned. The body of the for loop gets expanded for each step, but at that
+time there is no longer a variable \type {i}. The same is true for variables in:
+
+\starttyping
+def foo(expr x, y, delta) = draw (x+delta,y+delta) enddef ;
+\stoptyping
+
+We are still trying to get this right with the \LUA\ interface. Interesting is
+that when we were exploring this, we ran into quite some cases where we could
+make \METAPOST\ abort due some memory or stack overflow. Some are just bugs in
+the new code (due to the new number model) while others come with the design of
+the system: border cases that never seem to happen in interactive use while the
+library use assumes no interaction in case of errors.
+
+In \CONTEXT\ there are more features and helpers than shown here but these are
+discussed in the \METAFUN\ manual.
+
+\stopsection
+
+\stopchapter
+
+\stopcomponent
+
+% \startMPcode{doublefun}
+% numeric n ; n := 123.456 ;
+% lua("print('>>>>>>>>>>>> number',mp.get.number('n'))") ;
+% lua("print('>>>>>>>>>>>> number',mp.get.boolean('n'))") ;
+% lua("print('>>>>>>>>>>>> number',mp.get.string('n'))") ;
+% boolean b ; b := true ;
+% lua("print('>>>>>>>>>>>> boolean',mp.get.number('b'))") ;
+% lua("print('>>>>>>>>>>>> boolean',mp.get.boolean('b'))") ;
+% lua("print('>>>>>>>>>>>> boolean',mp.get.string('b'))") ;
+% string s ; s := "TEST" ;
+% lua("print('>>>>>>>>>>>> string',mp.get.number('s'))") ;
+% lua("print('>>>>>>>>>>>> string',mp.get.boolean('s'))") ;
+% lua("print('>>>>>>>>>>>> string',mp.get.string('s'))") ;
+% \stopMPcode
+