Is there some annotation that I can use to give permission to the compiler to elide e.g.
x = foo(y) if
x is not used? And would it possibly work across function-call boundaries? I’d like to do something like
function foo(input) println("foo") 2 * input end function bar(input) println("bar") 2 + input end function baz(x, y) println("baz") x / y end function compute(input, callback) x = foo(input) y = bar(input) z = baz(x, y) return callback((;x, y, z)) end
and depending on what fields
callback accesses, perhaps only
foo is called. Or only
bar. Or both. Or all three.
julia> compute(10, nt -> nt.x) foo bar baz 20
but I’d like to annotate the function calls so that only
foo is printed.
I could turn all computations into thunks:
function foo_thunk(input) ()->(println("foo"); 2 * input()) end function bar_thunk(input) ()->(println("bar"); 2 + input()) end function baz_thunk(x, y) ()->(println("baz"); x() / y()) end function compute_thunk(input, callback) x = foo_thunk(()->input) y = bar_thunk(()->input) z = baz_thunk(x, y) return callback((;x, y, z)) end
julia> compute_thunk(10, nt -> nt.x() + nt.z()) foo baz foo bar 21.666666666666668
but I do not want
foo to be computed twice. Caching/memoizing the thunks would be a solution, but I do not think I’m looking for that. I want to avoid the dynamic memory allocations and dynamic checks for whether values have already been computed. Otherwise I’m relying on other compiler optimizations like escape analysis, which would likely give suboptimal results due to the function-call boundary. (Though for any of this to make sense,
callback has to be inlined anyway)
Truly, I’m looking for a solution that is basically equivalent to deleting any unused fields from the
NamedTuple, and deleting the lines where the values for those fields are computed.
I don’t expect anything to be elided if there is e.g. any
getproperty(nt, fieldname), for a
fieldname that is not marked
I’m all but certain that
Base.@pure is the wrong answer.