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
(::typeof(Main.foo))(x::Int)=x
end
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}:
:Testme
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(Main.foo))[1].module
Main
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.
Since
julia> Base.Bottom <: (NamedTuple{(:a, :b),T} where T<:Tuple)
true
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 Type.body.name.mt
. 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.