Can I get the type of an inner function without calling the outer function?

In the code below, bar is an inner function. I’d like to get its type from outside the outer function foo, but without calling foo. Is that possible?

function foo()
    bar() = 1
end

T = typeof(foo()) # var"#bar#8"

# is there a way to get the same thing as `T` without calling `foo`? 
# I might have though something like this could work (but it doesn't):
foo.bar

Note, in my real example, foo doesn’t return bar, so I can’t use Core.Compiler.return_type.

Probably someone has a better answer than me, but I would bet on not being possible. If the closure is created inside the function, then it may not exist before the function is called and, consequently, their type also do no exist. You can’t lift the function to the global scope?

1 Like

It is there in the lowered code, though:

julia> function foo()
           bar() = 1
       end
foo (generic function with 1 method)

julia> @code_lowered foo()
CodeInfo(
1 ─     bar = %new(Main.:(var"#bar#1"))
└──     return bar
)

However, perhaps he can explain why he wants that? Might be one of these cases where there is another way to deal with the actual problem.

(does that type provides any information more than just the function name?)

typeof((@code_typed foo()).first.ssavaluetypes[1].val)
2 Likes

Interesting solutions, thanks. My main purpose is actually for AutoPrealloaction.jl which uses Cassette.jl to overdub code to preallocate memory, and to make something work, I need to write a custom overdub for an inner function of some other package, hence I need the inner function’s type.

The solution above basically works but is definitely way too internal-dependent and also not stable w.r.t. future code changes inside foo for it to be a real solution, but its certainly interesting to see it:

using Cassette

function foo()
    bar() = 1
    bar()
end

T = eval(@code_lowered(foo()).code[1].args[2].args[1].name)

# make bar return 2 instead
Cassette.@context Ctx
Cassette.overdub(ctx::Ctx, ::T) = 2 

Cassette.overdub(Ctx(), foo) # 2, works
2 Likes

I guess a straightforward solution would be to make a PR to that package, moving that function out, and explaining why.

If it needs to remain an inner function for some reason, you could instead do a PR with something like:

function foo(;getbar=nothing)
    bar() = ...
    getbar === Val(true) && return bar
    ...
end

Now that I think about it: Anything you could do with a PR, you could probably also do from your own code using Casette, couldn’t you?