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
|
Under the Hood
==============
.. module:: fun
The section shed some light on the internal library structure and working
principles.
Iterators
---------
A basic primitive of the library after functions is an iterator. Most functions
takes an iterator and returns a new iteraror(s). Iterators all the way down!
[#iterators]_.
The simplest iterator is (surprise!) :func:`pairs` and :func:`ipairs`
Lua functions. Have you ever tried to call, say, :func:`ipairs` function
without using it inside a ``for`` loop? Try to do that on any Lua
implementation:
.. _iterator_triplet:
.. code-block:: bash
> =ipairs({'a', 'b', 'c'})
function: builtin#6 table: 0x40f80e38 0
The function returned three strange values which look useless without a ``for``
loop. We call these values **iterator triplet**.
Let's see how each value is used for:
``gen`` -- first value
A generating function that can produce a next value on each iteration.
Usually returns a new ``state`` and iteration values (multireturn).
``param`` -- second value
A permanent (constant) parameter of a generating function is used to create
specific instance of the generating function. For example, a table itself
for ``ipairs`` case.
``state`` -- third value
A some transient state of an iterator that is changed after each iteration.
For example, an array index for ``ipairs`` case.
Try to call ``gen`` function manually:
.. code-block:: lua
> gen, param, state = ipairs({'a', 'b', 'c'})
> =gen(param, state)
1 a
The ``gen`` function returned a new state ``1`` and the next iteration
value ``a``. The second call to ``gen`` with the new state will return the next
state and the next iteration value. When the iterator finishes to the end
the ``nil`` value is returned instead of the next state.
**Please do not panic!** You do not have to use these values directly.
It is just a nice trick to get ``for .. in`` loop working in Lua.
Iterations
----------
What happens when you type the following code into a Lua console::
for _it, x in ipairs({'a', 'b', 'c'}) do print(x) end
According to Lua reference manual [#lua_for]_ the code above is equivalent to::
do
-- Initialize the iterator
local gen, param, state = ipairs({'a', 'b', 'c'})
while true do
-- Next iteration
local state, var_1, ยทยทยท, var_n = gen(param, state)
if state == nil then break end
-- Assign values to our variables
_it = state
x = var_1
-- Execute the code block
print(x)
end
end
What does it mean for us?
* An iterator can be used together with ``for .. in`` to generate a loop
* An iterator is fully defined using ``gen``, ``param`` and ``state`` iterator
triplet
* The ``nil`` state marks the end of an iteration
* An iterator can return an arbitrary number of values (multireturn)
* It is possible to make some wrapping functions to take an iterator and
return a new modified iterator
**The library provides a set of iterators** that can be used like ``pairs``
and ``ipairs``.
Iterator Types
--------------
Pure functional iterators
`````````````````````````
Iterators can be either pure functional or have some side effects and returns
different values for some initial conditions [#pure_function]_. An **iterator is
pure functional** if it meets the following criteria:
- ``gen`` function always returns the same values for the same ``param`` and
``state`` values (idempotence property)
- ``param`` and ``state`` values are not modified during ``gen`` call and
a new ``state`` object is returned instead (referential transparency
property).
Pure functional iterators are very important for us. Pure functional iterator
can be safety cloned or reapplied without creating side effects. Many library
function use these properties.
Finite iterators
````````````````
Iterators can be **finite** (sooner or later end up) or **infinite**
(never end).
Since there is no way to determine automatically if an iterator is finite or
not [#turing]_ the library function can not automatically resolve infinite
loops. It is your obligation to do not pass infinite iterator to reducing
functions.
Tracing JIT
-----------
Tracing just-in-time compilation is a technique used by virtual machines to
optimize the execution of a program at runtime. This is done by recording a
linear sequence of frequently executed operations, compiling them to native
machine code and executing them.
First profiling information for loops is collected. After a hot loop has been
identified, a special tracing mode is entered which records all executed
operations of that loop. This sequence of operations is called a **trace**.
The trace is then optimized and compiled to machine code (trace). When this
loop is executed again the compiled trace is called instead of the program
counterpart [#tracing_jit]_.
Why the tracing JIT is important for us? The LuaJIT tracing compiler can detect
tail-, up- and down-recursion [#luajit-recursion]_, unroll compositions of
functions and inline high-order functions [#luajit-optimizations]_.
All of these concepts make the foundation for functional programming.
.. [#iterators] http://en.wikipedia.org/wiki/Turtles_all_the_way_down
.. [#lua_for] http://www.lua.org/manual/5.2/manual.html#3.3.5
.. [#pure_function] http://en.wikipedia.org/wiki/Pure_function
.. [#turing] `Proved by Turing <http://en.wikipedia.org/wiki/Halting_problem>`_
.. [#tracing_jit] http://en.wikipedia.org/wiki/Tracing_just-in-time_compilation
.. [#luajit-recursion] http://lambda-the-ultimate.org/node/3851#comment-57679
.. [#luajit-optimizations] http://wiki.luajit.org/Optimizations
|