Accessing all definitions in a module

For a program analysis tool I’m working on, I need to know about all available functions. However, the traditional suggestion using names does not work for me, with the canonical example happening with NamedTuples. The (internal) constructor of a NamedTuple is written like

(::Type{NamedTuple{names,T} where T<:Tuple})(args::Tuple) where names = ....

This means that it has no nice “name” that it can be found under; you need to know that there’s a two-argument form of NamedTuple to look it up with. This then means that names(Base) doesn’t find it, a common problem to all methods defined with the (::Type{T})(...) idiom.

It’s possible to see this on a small example.

foo() = "hi"
module Testme

using .Testme
foo(2) # returns 2

However, if we try to find this definition with names, we find:

> names(Testme)
1-element Array{Symbol,1}:

Is there a way to access all methods, regardless if they have a distinct name or not?

Functions and types have a parentmodule, but I am not sure how that particular method in your example is associated with Testme. If anything,

julia> methodswith(typeof([1].module

which is parentmodule(foo) too. Code location here may be irrelevant for the purposes of being in a module.

That’s a good point, this issue isn’t really one that’s linked to modules. Sure, in this example it’s possible to identify the inner foo from calling methods(foo), and we can find out about foo from names(module) (which is why the module comes into it) which handles most of the cases. This is how most of the answers to “how do I find all methods” that I’ve been able to find work. However, this doesn’t solve my actual problem, which is the NamedTuple example. If we do methods(NamedTuple), we get

# 1 method for type constructor:
[1] (::Type{NamedTuple})() in Core at boot.jl:542

which is fine, except that’s not the constructor that the (key=value,...) sugar turns into. We need to know that it’s actually defined on the type NamedTuple{names,T} where T<:Tuple where names, at which point we get

julia> methods(NamedTuple{(:a,:b),T} where T<:Tuple)
# 3 methods for type constructor:
[1] (::Type{NamedTuple{names,T} where T<:Tuple})(args::Tuple) where names in Core at boot.jl:549
[2] (::Type{NamedTuple{names,T} where T<:Tuple})(nt::NamedTuple) where names in Base at namedtuple.jl:88
[3] (::Type{NamedTuple{names,T} where T<:Tuple})(itr) where names in Base at namedtuple.jl:98

names doesn’t tell you about the existence of methods defined like this, which is where the problem arises: these constructors don’t appear to have names, and aren’t actually “seen” as constructors per se (since if we look them up with NamedTuple nothing shows up). I don’t even really know how to access these programmatically if you’re not given the magic type a priori.

I don’t really care about what module defined the function per se, that was just driven by assuming that whatever mechanism there was would work like names does. Something that just gave me a big list of all method definitions currently loaded would be fantastic, but I don’t know if that’s possible.

I would try something like

methodswith(Base.Bottom; supertypes = true)

Hmm, that’s really promising but doesn’t seem to solve the NamedTuple example; could the problem be that it’s seen as a type constructor?

For example,

example = first(methods(NamedTuple{(:a, :b),T} where T<:Tuple))
println(example in methods(NamedTuple{(:a, :b),T} where T<:Tuple)) # true
println(example in methodswith(Base.Bottom; supertypes = true)) # false

Digging into it more, it looks like methodswith is using names internally (see here), so I don’t think it will address this particular issue.


julia> Base.Bottom <: (NamedTuple{(:a, :b),T} where T<:Tuple)

I think it is worth opening an issue (but you may want to wait a bit for other suggestions).

I think your use case is a reasonable one and there should be an API for it.

As a quick follow up, if you’re interested, I found a way to solve my particular problem, though it’s not a general (or particularly nice) solution. These “magic” unbound functions all live on the Type method table (as they’re bound to specific types in the receiver position) and are associated with Julia internally maintains a special reference to this method table for this reason. I haven’t ran into problems with just loading this set of methods into my analysis yet, but we’ll see.