Skip to content

Commit df601ea

Browse files
docs: fixes and updates (#130)
* Many typo/grammar fixes and clarification improvements * Replaced the old pre-Julia-1.0 style iterator interface shown in the "internals" page with the correct `iterate` interface that's actually used by the package * Fix install instructions to import `Pkg` first, and sync the authors list with what's in the README
1 parent b9ce579 commit df601ea

File tree

4 files changed

+54
-38
lines changed

4 files changed

+54
-38
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ end
5959
for fib in fibonacci(10)
6060
println(fib)
6161
end
62+
```
6263

63-
# Example with specifies the length
64+
```julia
65+
# Example which specifies the length
6466
using ResumableFunctions
6567

6668
@resumable length=n^2 function fibonacci(n::Int) :: Int

docs/src/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ResumableFunctions
22

3-
C# style generators a.k.a. semi-coroutines for Julia.
3+
*C# style generators a.k.a. semi-coroutines for Julia.*
44

55
C# has a convenient way to create iterators using the `yield return` statement. The package `ResumableFunctions` provides the same functionality for the Julia language by introducing the `@resumable` and the `@yield` macros. These macros can be used to replace the `Task` switching functions `produce` and `consume` which were deprecated in Julia v0.6. `Channels` are the preferred way for inter-task communication in julia v0.6+, but their performance is subpar for iterator applications.
66

@@ -40,12 +40,14 @@ end
4040

4141
`ResumableFunctions` is a registered package and can be installed by running:
4242
```julia
43+
using Pkg
4344
Pkg.add("ResumableFunctions")
4445
```
4546

4647
## Authors
4748

4849
* Ben Lauwens, Royal Military Academy, Brussels, Belgium.
50+
* JuliaDynamics and QuantumSavory volunteers.
4951

5052
## License
5153

docs/src/internals.md

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
# Internals
22

3-
The macro `@resumable` transform a function definition into a finite state-machine, i.e. a callable type holding the state and references to the internal variables of the function and a constructor for this new type respecting the method signature of the original function definition. When calling the new type a modified version of the body of the original function definition is executed:
4-
- a dispatch mechanism is inserted at the start to allow a non local jump to a label inside the body;
5-
- the `@yield` statement is replaced by a `return` statement and a label placeholder as endpoint of a non local jump;
6-
- `for` loops are transformed in `while` loops and
7-
- `try`-`catch`-`finally`-`end` expressions are converted in a sequence of `try`-`catch`-`end` expressions with at the end of the `catch` part a non local jump to a label that marks the beginning of the expressions in the `finally` part.
8-
The two last transformations are needed to overcome the limitations of the non local jump macros `@goto` and `@label`.
3+
The macro `@resumable` transforms a function definition into a finite state-machine, i.e.
4+
* a callable type holding the state and references to the internal variables of the function, and
5+
* a constructor for this new type respecting the method signature of the original function definition.
6+
7+
When calling an instance of this new type, a modified version of the body of the original function definition is executed:
8+
- a dispatch mechanism is inserted at the start to allow a non-local jump to a label inside the body;
9+
- the `@yield` statement is replaced by a `return` statement and a label placeholder as endpoint of a non-local jump;
10+
- `for` loops are transformed into `while` loops; and
11+
- `try`-`catch`-`finally`-`end` expressions are converted into a sequence of `try`-`catch`-`end` expressions; at the end of the `catch` part, a non-local jump is inserted to a label that marks the beginning of the expressions in the `finally` part.
12+
13+
The last two transformations are needed to overcome the limitations of the non-local jump macros `@goto` and `@label`.
914

1015
The complete procedure is explained using the following example:
1116

@@ -27,18 +32,19 @@ The function definition is split by `MacroTools.splitdef` in different parts, eg
2732

2833
## For loops
2934

30-
`for` loops in the body of the function definition are transformed in equivalent while loops:
35+
`for` loops in the body of the function definition are transformed into equivalent `while` loops:
3136

3237
```julia
3338
begin
3439
a = 0
3540
b = 1
3641
_iter = 1:n-1
37-
_iterstate = start(_iter)
38-
while !done(_iter, _iterstate)
39-
i, _iterstate = next(_iter, _iterstate)
42+
state = iterate(_iter)
43+
while state !== nothing
44+
i, _iterstate = state
4045
@yield a
4146
a, b = b, a + b
47+
state = iterate(_iter, _iterstate)
4248
end
4349
a
4450
end
@@ -79,9 +85,9 @@ mutable struct ##123 <: ResumableFunctions.FiniteStateMachineIterator
7985
end
8086
```
8187

82-
## Call definition
88+
## Caller definition
8389

84-
A call function is constructed that creates the previously defined composite type. This function satisfy the calling convention of the original function definition and is returned from the macro:
90+
A caller function is constructed that creates the previously defined composite type. This function satisfies the calling convention of the original function definition and is returned from the macro:
8591

8692
```julia
8793
function fibonacci(n::Int)
@@ -93,7 +99,7 @@ end
9399

94100
## Transformation of the body
95101

96-
In 6 steps the body of the function definition is transformed into a finite state-machine.
102+
In 6 steps the body of the function definition is transformed into a finite state-machine:
97103

98104
### Renaming of slots
99105

@@ -104,11 +110,12 @@ begin
104110
_fsmi.a = 0
105111
_fsmi.b = 1
106112
_fsmi._iter = 1:n-1
107-
_fsmi._iterstate = start(_fsmi._iter)
108-
while !done(_fsmi._iter, _fsmi._iterstate)
109-
_fsmi.i, _fsmi._iterstate = next(_fsmi._iter, _fsmi._iterstate)
113+
state = iterate(_fsmi._iter)
114+
while state !== nothing
115+
_fsmi.i, _fsmi._iterstate = state
110116
@yield _fsmi.a
111117
_fsmi.a, _fsmi.b = _fsmi.b, _fsmi.a + _fsmi.b
118+
state = iterate(_fsmi._iter, _fsmi._iterstate)
112119
end
113120
_fsmi.a
114121
end
@@ -132,76 +139,79 @@ begin
132139
_fsmi.a = 0
133140
_fsmi.b = 1
134141
_fsmi._iter = 1:n-1
135-
_fsmi._iterstate = start(_fsmi._iter)
136-
while !done(_fsmi._iter, _fsmi._iterstate)
137-
_fsmi.i, _fsmi._iterstate = next(_fsmi._iter, _fsmi._iterstate)
142+
state = iterate(_fsmi._iter)
143+
while state !== nothing
144+
_fsmi.i, _fsmi._iterstate = state
138145
@yield _fsmi.a
139146
_arg isa Exception && throw(_arg)
140147
_fsmi.a, _fsmi.b = _fsmi.b, _fsmi.a + _fsmi.b
148+
state = iterate(_fsmi._iter, _fsmi._iterstate)
141149
end
142150
_fsmi.a
143151
end
144152
```
145153

146154
### Try catch finally end block handling
147155

148-
`try`-`catch`-`finally`-`end` expressions are converted in a sequence of `try`-`catch`-`end` expressions with at the end of the `catch` part a non local jump to a label that marks the beginning of the expressions in the `finally` part.
156+
`try`-`catch`-`finally`-`end` expressions are converted into a sequence of `try`-`catch`-`end` expressions; at the end of the `catch` part, a non-local jump is inserted to a label that marks the beginning of the expressions in the `finally` part.
149157

150158
### Yield transformation
151159

152-
The `@yield` statement is replaced by a `return` statement and a label placeholder as endpoint of a non local jump:
160+
The `@yield` statement is replaced by a `return` statement and a label placeholder as target of a non-local jump:
153161

154162
```julia
155163
begin
156164
_fsmi.a = 0
157165
_fsmi.b = 1
158166
_fsmi._iter = 1:n-1
159-
_fsmi._iterstate = start(_fsmi._iter)
160-
while !done(_fsmi._iter, _fsmi._iterstate)
161-
_fsmi.i, _fsmi._iterstate = next(_fsmi._iter, _fsmi._iterstate)
167+
state = iterate(_fsmi._iter)
168+
while state !== nothing
169+
_fsmi.i, _fsmi._iterstate = state
162170
_fsmi._state = 0x01
163171
return _fsmi.a
164172
@label _STATE_1
165173
_fsmi._state = 0xff
166174
_arg isa Exception && throw(_arg)
167175
_fsmi.a, _fsmi.b = _fsmi.b, _fsmi.a + _fsmi.b
176+
state = iterate(_fsmi._iter, _fsmi._iterstate)
168177
end
169178
_fsmi.a
170179
end
171180
```
172181

173182
### Dispatch mechanism
174183

175-
A dispatch mechanism is inserted at the start of the body to allow a non local jump to a label inside the body:
184+
A dispatch mechanism is inserted at the start of the body to allow a non-local jump to a label inside the body:
176185

177186
```julia
178187
begin
179-
_fsmi_state == 0x00 && @goto _STATE_0
180-
_fsmi_state == 0x01 && @goto _STATE_1
188+
_fsmi._state == 0x00 && @goto _STATE_0
189+
_fsmi._state == 0x01 && @goto _STATE_1
181190
error("@resumable function has stopped!")
182191
@label _STATE_0
183192
_fsmi._state = 0xff
184193
_arg isa Exception && throw(_arg)
185194
_fsmi.a = 0
186195
_fsmi.b = 1
187196
_fsmi._iter = 1:n-1
188-
_fsmi._iterstate = start(_fsmi._iter)
189-
while !done(_fsmi._iter, _fsmi._iterstate)
190-
_fsmi.i, _fsmi._iterstate = next(_fsmi._iter, _fsmi._iterstate)
197+
state = iterate(_fsmi._iter)
198+
while state !== nothing
199+
_fsmi.i, _fsmi._iterstate = state
191200
_fsmi._state = 0x01
192201
return _fsmi.a
193202
@label _STATE_1
194203
_fsmi._state = 0xff
195204
_arg isa Exception && throw(_arg)
196205
_fsmi.a, _fsmi.b = _fsmi.b, _fsmi.a + _fsmi.b
206+
state = iterate(_fsmi._iter, _fsmi._iterstate)
197207
end
198208
_fsmi.a
199209
end
200210
```
201211

202212
## Making the type callable
203213

204-
A function is defined with one argument `_arg`:
214+
A function is defined with the above code as its body, accepting one argument `_arg`:
205215

206216
```julia
207217
function (_fsmi::##123)(_arg::Any=nothing)
@@ -214,15 +224,16 @@ function (_fsmi::##123)(_arg::Any=nothing)
214224
_fsmi.a = 0
215225
_fsmi.b = 1
216226
_fsmi._iter = 1:n-1
217-
_fsmi._iterstate = start(_fsmi._iter)
218-
while !done(_fsmi._iter, _fsmi._iterstate)
219-
_fsmi.i, _fsmi._iterstate = next(_fsmi._iter, _fsmi._iterstate)
227+
state = iterate(_fsmi._iter)
228+
while state !== nothing
229+
_fsmi.i, _fsmi._iterstate = state
220230
_fsmi._state = 0x01
221231
return _fsmi.a
222232
@label _STATE_1
223233
_fsmi._state = 0xff
224234
_arg isa Exception && throw(_arg)
225235
_fsmi.a, _fsmi.b = _fsmi.b, _fsmi.a + _fsmi.b
236+
state = iterate(_fsmi._iter, _fsmi._iterstate)
226237
end
227238
_fsmi.a
228239
end

docs/src/manual.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ ERROR: @resumable function has stopped!
188188
DocTestSetup = nothing
189189
```
190190

191-
When the `@resumable function` returns normally an error will be thrown if called again.
191+
When the `@resumable function` returns normally (i.e. at the end of the function rather than at a `@yield` point), an error will be thrown if called again.
192192

193193
## Two-way communication
194194

@@ -320,7 +320,7 @@ DocTestSetup = nothing
320320

321321
## Parametric `@resumable` functions
322322

323-
Type parameters can be specified with a `where` clause:
323+
Type parameters can be specified with a normal Julia `where` clause:
324324

325325
```@meta
326326
DocTestSetup = quote
@@ -388,6 +388,7 @@ end
388388
@resumable function arrays_of_tuples()
389389
for u in [[(1,2),(3,4)], [(5,6),(7,8)]]
390390
for i in 1:2
391+
local val
391392
let i=i
392393
val = [a[i] for a in u]
393394
end

0 commit comments

Comments
 (0)