Trying to grasp staged functions in Julia 0.4, I found the new documentation
<https://github.com/JuliaLang/julia/pull/10673> and this issue
<https://github.com/JuliaLang/julia/pull/7474> very helpful. (Note from the
issue that the syntax may still change from `stagedfunction` to, perhaps,
`@generated function`).
There are tons of introductory examples of how to do compile-time
factorials in languages with metaprogramming and I was wondering how to do
this in Julia. I've learned by doing this and my best attempt so far is
(using another 0.4 feature with Val{T}) :
stagedfunction fac{N}(::Type{Val{N}})
N == 1 ? z = 1 : z = N*fac(Val{N-1});
println(z);
:($z)
end
I did not see a way to get rid of the argument in the function above, since
Julia does not allow the definition of fac{N}() without N being used in the
signature, so the result looks a bit awkward. I also failed to achieve the
same result using macros, as it seems macros can't be recursive, but maybe
I just missed it (and I wanted to use stagedfunctions anyway). Any better
ideas?
The println in the example is helpful to see what is actually happening:
julia> fac(Val{4})
1
2
6
24
24
julia> fac(Val{7})
120
720
5040
5040
julia> fac(Val{7})
5040
julia> @code_native fac(Val{6})
.text
Filename: no file
Source line: 0
pushq %rbp
movq %rsp, %rbp
movl $720, %eax # imm = 0x2D0
Source line: 0
popq %rbp
ret
The cool thing here is that the function works as if Julia automagically
caches the previous result. The first computation of 7! just continues
where 4! left off and the next computation is instant, as the instructions
for Val{6} also show. Can staged functions be used (or abused) to cache
one-time computational results during a session?
A simpler alternative I was hoping for is the following:
fac{N}(::Type{Val{N}}) = N*fac(Val{N-1})
fac(::Val{1}) = 1
but in this case the compiler does not seem to inline the result of, say,
fac(Val{3}) in functions that use it.