On Thursday, April 9, 2015 at 3:33:29 PM UTC+2, Daan Huybrechs wrote:
>
> 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?
>
When you ran fac(Val{4}) you compiled fac(Val{4}), fac(Val{3}), fac(Val{2})
and fac(Val{1}). These are all folded down to just returning a constant.
When you later run
fac(Val{7}) and it iterates down to fac(Val{4}), this method is already
in the method cache so it is just runs and returns the constant. The
println only happens during compilation.
>
> 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.
>
>