This seems a bit aggressive:
Code 1:
julia> @generated f(x) = :( map(i->i, x) )
f (generic function with 1 method)
julia> f((1,2,3))
ERROR: The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator.
How is the function body not pure? The lambda isn’t capturing anything.
Here’s a simpler example:
Code 2:
julia> @generated g() = :( x -> x )
g (generic function with 1 method)
julia> g()
ERROR: The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator.
We can confirm that g
is pure when not @generated
:
Code 3:
julia> g() = x->x
g (generic function with 1 method)
julia> g() === g()
true
I suppose maybe in code #1 it’s trying to cover the possibility that this call to an externally-defined function map
is impure? But if that was it, this should work:
Code 4:
julia> @generated k(x) = :( α=y->y; α(x) )
k (generic function with 1 method)
julia> k(1)
ERROR: The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator.
For my use case I wanted a lambda with a capture, which upon construction with its capture I believe to be pure, nested within a greater function I believe also to be pure similar to this:
Code 5:
julia> @generated h(x, y) = :( map(i->x[i], y) )
h (generic function with 1 method)
julia> h((1,2,3), (1,2,3))
ERROR: The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator.
I found a way around this by eventually giving up on @generated
and instead breaking the function into several and using dispatch, but it took a while for me to concede defeat so it made me curious.
According to the docs:
- Generated functions must not mutate or observe any non-constant global state (including, for example, IO, locks, non-local dictionaries, or using
hasmethod
). This means they can only read global constants, and cannot have any side effects. In other words, they must be completely pure. Due to an implementation limitation, this also means that they currently cannot define a closure or generator.
Is the issue maybe that we are simply throwing an erroneous error message, i.e. it is accusing us of defining an impure function when in reality it’s just a limitation of the current implementation?
Or is the issue that the act of hoisting the lambda to global scope on the first function invocation (and thus changing global state) makes the function impure?
Or am I misunderstanding the whole thing somehow?