What’s the preferred way to write recursive anonymous functions?
Consider a demo function:
julia> function fib(n)
n ≤ 1 && return n
return fib(n-1) + fib(n-2)
end
fib (generic function with 1 method)
julia> @btime fib(10)
206.726 ns (0 allocations: 0 bytes)
55
Suppose I want to make an “anonymous” version of this function. Of course, the function needs a name to call itself, but maybe I can give it a name in a local scope:
julia> fibby=let
function fib(n)
n ≤ 1 && return n
return fib(n-1) + fib(n-2)
end
end
(::var"#fib#1") (generic function with 1 method)
julia> @btime $fibby(10)
3.725 μs (0 allocations: 0 bytes)
55
It works, but it its performance takes a big hit—running over 10x slower.
It goes type-unstable.
julia> @code_warntype fibby(10)
MethodInstance for (::var"#fib#1")(::Int64)
from (::var"#fib#1")(n) in Main at REPL[4]:2
Arguments
#self#::var"#fib#1"
n::Int64
Locals
fib@_3::Union{}
fib@_4::Union{}
Body::Any
1 ─ %1 = (n ≤ 1)::Bool
└── goto #3 if not %1
2 ─ return n
3 ─ %4 = Core.getfield(#self#, :fib)::Core.Box
│ %5 = Core.isdefined(%4, :contents)::Bool
└── goto #5 if not %5
4 ─ goto #6
5 ─ Core.NewvarNode(:(fib@_3))
└── fib@_3
6 ┄ %10 = Core.getfield(%4, :contents)::Any
│ %11 = (n - 1)::Int64
│ %12 = (%10)(%11)::Any
│ %13 = Core.getfield(#self#, :fib)::Core.Box
│ %14 = Core.isdefined(%13, :contents)::Bool
└── goto #8 if not %14
7 ─ goto #9
8 ─ Core.NewvarNode(:(fib@_4))
└── fib@_4
9 ┄ %19 = Core.getfield(%13, :contents)::Any
│ %20 = (n - 2)::Int64
│ %21 = (%19)(%20)::Any
│ %22 = (%12 + %21)::Any
└── return %22
What if we put it inside another function?
julia> function fibby2(n)
function fib(n)
n ≤ 1 && return n
return fib(n-1) + fib(n-2)
end
return fib(n)
end
fibby2 (generic function with 1 method)
julia> @btime fibby2(10)
3.688 μs (2 allocations: 32 bytes)
55
Same thing.
Is this supposed to happen? What am I missing?