@@ -181,8 +181,10 @@ Base.iterate(::One, ::Any) = nothing
181
181
Thunk(()->v)
182
182
A thunk is a deferred computation.
183
183
It wraps a zero argument closure that when invoked returns a differential.
184
+ `@thunk(v)` is a macro that expands into `Thunk(()->v)`.
184
185
185
- Calling that thunk, calls the wrapped closure.
186
+
187
+ Calling a thunk, calls the wrapped closure.
186
188
`extern`ing thunks applies recursively, it also externs the differial that the closure returns.
187
189
If you do not want that, then simply call the thunk
188
190
@@ -199,6 +201,22 @@ Thunk(var"##8#10"())
199
201
julia> t()()
200
202
3
201
203
```
204
+
205
+ ### When to `@thunk`?
206
+ When writing `rrule`s (and to a lesser exent `frule`s), it is important to `@thunk`
207
+ appropriately. Propagation rule's that return multiple deriviatives are able to not
208
+ do all the work of computing all of them only to have just one used.
209
+ By `@thunk`ing the work required for each, they can only be computed when needed.
210
+
211
+ #### So why not thunk everything?
212
+ `@thunk` creates a closure over the expression, which is basically a struct with a
213
+ field for each variable used in the expression (closed over), and call overloaded.
214
+ If this would be equal or more work than actually evaluating the expression then don't do
215
+ it. An example would be if the expression itself is just wrapping something in a struct.
216
+ Such as `Adjoint(x)` or `Diagonal(x)`. Or if the expression is a constant, or is
217
+ itself a `Thunk`.
218
+ If you got the expression from another `rrule` (or `frule`), you don't need to
219
+ `@thunk` it since it will have been thunked if required, by the defining rule.
202
220
"""
203
221
struct Thunk{F} <: AbstractDifferential
204
222
f:: F
0 commit comments