# How to determine the methods for a callable object without instantiating it?

**URL:** https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618
**Category:** General Usage
**Created:** [August 8, 2023, 10:58pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618 "2023-08-08T22:58:26Z")
**Posts on this page:** 19
**Page:** 1

<div class="post-metadata">

### Author: ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)
#### Post date: [August 8, 2023, 10:58pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/1 "2023-08-08T22:58:26Z")

</div>

Suppose I have the following:

```julia
struct A{T} x::T end
(a::A)(y) = a.x+y

```

Then I can say

```julia
julia> a = A(1); methods(a)
# 1 method for callable object:
 [1] (a::A)(y)
     @ Main REPL[2]:1

```

This list only depends on the type of `a`, not on the element `a` itself. Is it possible to get this list directly from the type `A{T}`, without instantiating any element?

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 9, 2023, 1:11am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/2 "2023-08-09T01:11:24Z")

</div>

Yes, it is possible. But from what I can see this is not supported yet.

However, you can use the following function to achieve the desired functionality:

```julia
function fancymethods(x)
    tsyg = Tuple{x,Vararg{Any}}
    temp = Base._methods_by_ftype(tsyg, -1, Base.get_world_counter())
    ms = Method[]
    for m in temp::Vector
        m = m::Core.MethodMatch
        push!(ms, m.method)
    end
    Base.MethodList(ms, x.name.mt)
end

```

It required a little digging, and I don’t have much time to explain everything right now (if a detailed explanation is needed, I can provide it later).

This is not defined under `Base` and I didn’t test it with types defined under various modules - so I cannot provide many guarantees at this point. I’ll make it more robust later.

The usage differs from parametric struct vs non-parametric.

Examples:

```julia
struct A{T}
    x::T
end
(a::A)(y) = a.x + y
(b::A)(x::String) = string(b.x) * x
(c::A{Float64})(x::Float64) = c.x + x

fancymethods(A{Any})

```

The above `fancymethods` call will produce:

```julia
# 2 methods for callable object:
 [1] (b::A)(x::String)
     @ Main ~/code/teach.jl/cobj.jl:5
 [2] (a::A)(y)
     @ Main ~/code/teach.jl/cobj.jl:4

```

You can see that the `A{Float64}` was ignored.

However, calling `fancymethods(A{Float64})` will produce, as expected:

```julia
# 3 methods for callable object:
 [1] (b::A)(x::String)
     @ Main ~/code/teach.jl/cobj.jl:5
 [2] (c::A{Float64})(x::Float64)
     @ Main ~/code/teach.jl/cobj.jl:6
 [3] (a::A)(y)
     @ Main ~/code/teach.jl/cobj.jl:4

```

The idea is that you don’t need to use an instance element; the type will suffice. But you need to be careful since `A{TypeA}` is not necessarily the same as `A{TypeB}` (e.g., the returned methods follow the dispatch rules).

For non-parametric types, things are straightforward:

```julia
struct B
    x::Int
end

(b::B)(x::String) = string(b.x) * x
(b::B)(y) = b.x + y

fancymethods(B)

```

Will produce:

```julia
# 2 methods for callable object:
 [1] (b::B)(x::String)
     @ Main ~/code/teach.jl/cobj.jl:20
 [2] (b::B)(y)
     @ Main ~/code/teach.jl/cobj.jl:22

```

The `fancymethods` function I wrote above is specially designated to get the callable object methods from using the type instead of the instance (if no method is defined for the object, you’ll get an empty `MethodList`).

Rely on the standard `methods` for everything else.

Have fun.

P. S. I think this would be helpful to be part of `Base` (the API should be something like `methods(T, withobjectmethods=true)` - this would return both the constructors for T and the callable object methods - or maybe the keyword can be used to indicate that only the callable object-related methods should be returned).

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [August 9, 2023, 3:56am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/3 "2023-08-09T03:56:36Z")

</div>

> [@algunion](#):
>
> You can see that the `A{Float64}` was ignored.

That’s expected though. `A{Float64}` is not a subtype of `A{Any}`, both are concrete types that subtype `A`.

Playing with this a little, `fancymethods(A)` doesn’t work (throws error for `UnionAll`), and declared-`abstract` types will include the concrete subtype methods (I subtyped `B <: C` in your example to check), which I’d sometimes want to exclude. Still, it’s incredibly useful as is, I wish more reflection methods accepted a callable’s type.

> [@algunion](#):
>
> maybe the keyword can be used to indicate that only the callable object-related methods should be returned

I’d rather a keyword that specifies where I’m checking a callable or a callable’s type, so `methods(T, ..., :callable)` for `T(...)` methods and `methods(T, ..., :typeofcallable)` for `(::T)(...)` methods.

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 9, 2023, 4:13am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/4 "2023-08-09T04:13:59Z")

</div>

> [@Benny](#):
>
> Playing with this a little, `fancymethods(A)` doesn’t work (throws error for `UnionAll`), and declared-`abstract` types will include the concrete subtype methods (I subtyped `B <: C` in your example to check), which I’d sometimes want to exclude.

This extends the original functionality to include `UnionAll` scenario as well:

```julia
function fancymethods(x)
    tsyg = Tuple{x,Vararg{Any}}
    temp = Base._methods_by_ftype(tsyg, -1, Base.get_world_counter())
    ms = Method[]
    for m in temp::Vector
        m = m::Core.MethodMatch
        push!(ms, m.method)
    end
    mt = x isa UnionAll ? Base.unwrap_unionall(x).name.mt : x.name.mt
    Base.MethodList(ms, mt)
end

```

The `Base.get_methodtable` fallback seems sound and the few tests I am running are looking good. I’ll keep this approach until disproved by some weird outcome.

Now the `fancymethods(A{<:Any})` call works OK.

---

<div class="post-metadata">

### Author: ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)
#### Post date: [August 9, 2023, 1:23pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/5 "2023-08-09T13:23:41Z")

</div>

That’s great, thanks a lot! I also think that this functionality would be valuable in Base. If one adds it, then maybe one should allow empty lists to be returned, as is the case with `methods`:

```julia
julia> struct A end; methods(A())
# 0 methods for callable object

```

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 9, 2023, 2:47pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/6 "2023-08-09T14:47:46Z")

</div>

> [@matthias314](#):
>
> If one adds it, then maybe one should allow empty lists to be returned

Updated the solution to reflect this.

This can be easily adapted to work via `methods` the way @Benny suggested.

@gdalle, if you find this valuable and already have the Julia repo setup, you might find this as a low-hanging fruit - also, I think this is worthy of `Base` addition. Pointing you to the method that needs extending [here](https://github.com/JuliaLang/julia/blob/d99f2496ff6203625c7b2d2f1e9d71e33b0e7f06/base/reflection.jl#L1067C53-L1067C53).

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [August 9, 2023, 3:31pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/7 "2023-08-09T15:31:45Z")

</div>

@algunion I do like low-hanging fruit but these days I’m on a fruit-heavy diet already 🙂 Could this be your opportunity to overcome the impostor syndrome and venture bravely into PR-land?

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 9, 2023, 3:38pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/8 "2023-08-09T15:38:19Z")

</div>

> [@gdalle](#):
>
> Could this be your opportunity to overcome the impostor syndrome and venture bravely into PR-land?

I’ll do it this week sometime: it looks like more work to set up the whole repo deal than doing the PR 🙂 But I understand that is a _one-time_ thing.

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 13, 2023, 1:24am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/9 "2023-08-13T01:24:41Z")

</div>

Done.

> <https://github.com/JuliaLang/julia/pull/50898>
>
> \# Rationale
> 
> The inception of this PR started \[here\](https://discourse.juliala…ng.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618?u=algunion) by observing that there is no easy way to determine the methods for a callable object without instantiating it.
> 
> For the following scenario, there is no way to use \`methods\` and retrieve the \`method\` list of the callable object without actually instantiating it first:
> \`\`\`julia
> struct A{T} x::T end
> (a::A)(y) = a.x+y
> \`\`\`
> Calling \`methods(A}\` will return the constructor methods. Callable object methods are only retrievable (before this PR) by performing the \`methods(A(1))\` call.
> 
> This is not practical at all (especially if the object construction is expensive). But even more, since the instances are of concrete types, extending the callable object methods in the following way will imply multiple instances construction and multiple \`methods\` calls in order to get the complete method collection defined for different \`A{T}\` types. 
> \`\`\`julia
> (a::A{Float64})(x::Float64) = a.x + x
> (a::A{String})(x::String) = a.x \* x
> 
> \# A{Float64}(1.0) instance and A{String}("s") needed
> afloat = A(1.0)
> astring = A("s")
> 
> methods(afloat)
> methods(astring) 
> \`\`\`
> 
> \# The \`:callable\` way
> 
> The current PR introduces the \`:callable\` key to the \`methods\` functionality. The change is non-breaking.
> \`\`\`julia
> julia\> struct A{T}
> x::T
> end
> 
> \# making the object callable
> julia\> (a::A{Float64})(x::Float64) = a.x + x
> julia\> (a::A{String})(x::String) = a.x \* x
> 
> julia\> a = A(1.0)
> A{Float64}(1.0)
> 
> julia\> methods(a) == methods(A{Float64}, :callable)
> true
> 
> julia\> length(methods(A{\<:Any}, :callable)) == 2
> true
> \`\`\`
> 
> Instead of constructing an instance, the methods defined for a callable object can now be retrieved by specifying the type of the object and using the \`:callable\` keyword (this is valid: \`methods(a) == methods(A{Float64}, :callable)\`).
> 
> Additionally, getting the complete collection of methods defined for the callable object types derived from A{T} is possible by the usage of \`methods(A{\<:Any}, :callable)\`. Or if only certain subtypes are needed: \`methods(A{\<:AbstractString}, :callable)\`.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [August 13, 2023, 3:27am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/10 "2023-08-13T03:27:38Z")

</div>

The PR using that keyword `methods(T, :callable)` to find methods `(::T)(args...)` is a bit cryptic because every input is a callable, including functions, type constructors, and callable instances we can use `methods` for already. Earlier I tried to distinguish the current usage and the proposed type-level usage with `:callable` and `:typeofcallable`, but in retrospect `:callable` by itself gives little information. Perhaps just directly with `:instance` vs `:type`? `:instance` (or omitting the argument entirely) would do what `methods` does already, while `:type` does the new stuff, in other words `methods(f, [types], [module]) == methods(f, [types], [module], :instance) == methods(typeof(f), [types], [module], :type)`. Still not a final idea because that may be misconstrued as referring to the arguments, but `:callableinstance` and `:typeofcallable` seems a bit verbose.

It just occurred to me, `which` has a method that takes a type signature as a tuple type of the types of the callable and the arguments. That seems clearer and easier than a trailing keyword.

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 13, 2023, 11:37am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/11 "2023-08-13T11:37:50Z")

</div>

It didn’t seem feasible to keep the surface as simple as possible and cover all the cases without adding much code. And the simplest option seemed only to introduce a singular keyword - `:callable` (with the meaning of `callable object of type T`).

So, as a general rule, the following assumptions need to hold:

```julia
struct A{T}
    x::T
end

(a::A{Float64})(x::Float64) = a.x + x
(a::A{String})(x::String) = a.x * x

# make `a` instance
a = A(...)

methods(A) != methods(A, :callable)
methods(a) == methods(typeof(a), :callable)
methods(a) == methods(a, :callable)

methods(push!) == methods(push!, :callable)

```

So, using `:callable` on an instance will just be ignored.

I tend to agree with the semantics part of things: especially after reading your message, maybe `:instance/:type` would be a better fit. And I would also avoid the verbose variants at this point.

At the time of writing the code + docs, my brain pretty much used the `:callable` idea in the context of `callable object` or `functor` (and I would avoid using `functor`).

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 13, 2023, 11:50am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/12 "2023-08-13T11:50:18Z")

</div>

> [@Benny](#):
>
> `:instance` (or omitting the argument entirely) would do what `methods` does already, while `:type` does the new stuff,

Doesn’t `methods(T, :instance)` seems to imply returning the methods for the instance of `T`? Which is not the default behavior.

It feels like those discussions about discussions, in a way - we are in the meta realm now.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [August 13, 2023, 11:54am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/13 "2023-08-13T11:54:16Z")

</div>

Wish it had occurred to me sooner but I edited that comment to point out `which` has a (undocumented) method that takes a tuple type of the types of the callable and the arguments together e.g. `which(Tuple{typeof(+), Int, Int})`. Seems clearer because you know it’s a callable by it preceding arguments, and you know it’s the type of the callable because it comes with types of the arguments, don’t need to look out for a trailing keyword. An instance of that tuple type would just be call inputs e.g. `(+, 1, 2)`. This signature-type pattern shows up in other methods that just don’t make it to the documentation, so it seems like a precedence to adhere to. Just need to figure out how to write a signature without specified arguments…

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 13, 2023, 12:25pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/14 "2023-08-13T12:25:31Z")

</div>

A. S. For potential readers not aware of the full context: please be advised that the functionality discussed on this topic is not part of the Julia language at this point. You can use the function selected as `solution` if you need to achieve the behavior stated in the OP.

If we put aside for a moment the actual keyword name, what you say above is already compatible with both default `methods` implementation and the `:callable` flavor.

```julia
struct A{T}
    x::T
end

(a::A{Float64})(x::Float64) = a.x + x
(a::A{String})(x::String) = a.x * x
(a::A{String})(x::Symbol) = a.x * string(x)

# make A{String} instance
a = A("ha")

methods(a, Tuple{Symbol}) == methods(A{String}, Tuple{Symbol}, :callable)

```

Basically, the `:callable` just enables the caller to use the type instead of the instance in the `methods` calls and get out the same answer as you would get when using the instance.

The `methods` behavior is untouched in all other aspects.

Also, when printing the methods list, you always get the appropriate message: `methods for callable object` vs `methods for type constructor` (and the `methods for generic function`, etc. are left alone anyway).

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 13, 2023, 1:35pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/15 "2023-08-13T13:35:56Z")

</div>

One solution to drop the keyword usage altogether might be the creation (inside `Base`) of yet another convenience/wrapper type:

```julia
Instance{T}
    t::T
end

```

So `methods(T)` would return the type constructor methods (the current behavior), and `methods(Instance{T})` would return the methods defined for instances of `T` (e.g., methods for the callable objects).

While technically possible, the `Tuple{}` usage (specifically the way it is used in `which`) seems like departing from the regular use of `methods` (where you merely return methods for the first argument and optionally restrict/filter by a second `Tuple{}` argument).

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [August 13, 2023, 9:21pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/16 "2023-08-13T21:21:20Z")

</div>

> [@algunion](#):
>
> departing from the regular use of `methods` (where you merely return methods for the first argument and optionally restrict/filter by a second `Tuple{}` argument).

True, but we’re already departing from the regular use by invoking the callable’s type and possibly a new argument. Also checking the source code, `methods` with unspecified argument types does `methods(f, Tuple{Vararg{Any}}, mod)`, and ultimately does use a helper `_methods_by_ftype` that takes in a signature tuple type like `Tuple{typeof(f), Vararg{Any}}`.

If we let the user-level `methods` take in this type, we could also make a helper method to make it. I’m imagining that people don’t want to manually type out an entire signature type, which they often don’t even do for the arguments in `methods`, they may at most specify number of arguments. So this may help:

```julia
typesig(T,N) = Tuple{T, Vararg{Any, N}} # becomes Tuple{T, Any, Any, ...}
typesig(T) = Tuple{T, Vararg{Any}}
# potentially, methods(typesig(T))

```

Aesthetically, it doesn’t really save typing compared to a trailing key word, and it’s about the same clarity as `Instance{T}` with the key word in the lead. Downside of the latter is that `Instance{T}` itself is a callable struct, so it’s ambiguous whether `methods` treats it as callable instance (like it can now) or type (in which case `Instance{T}` itself becomes hard to inspect methods for). Admittedly, the tuple signature does the same thing because tuple types are callable (for example, you can do `methods(Tuple)` or `methods(Tuple{typeof(+), Int, Int})` now), but there’s more precedence for it. Either way could be breaking though, which needs to be put in a separate function.

A separate function certainly won’t have to worry about collisions with the current usage. It also occurs to me that this could just be released as a v0 package, which people can experiment with, without the promise of backward compatibility of merging into v1 Julia. People can also contribute `typeof(callable)` versions of other standard library reflection methods, and it may eventually be absorbed into base Julia.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [August 14, 2023, 2:55am UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/17 "2023-08-14T02:55:38Z")

</div>

Another idea for non-breaking trailing key words: `:f` vs `:ftype`, reflecting some of the naming conventions in reflection.jl. The documentation already shows `methods(f, [types], [module])` to use `f` to indicate a callable instance, so a new method’s documentation could say `methods(ftype, [types], [module], :ftype)`, concise and seems clear enough. I’m still partial to a clean signature type-based reflections package, but keeping the callable and the arguments separate in 1 existing function seems more realistic.

Also, we may have incorrectly assumed a new argument must be put at the end. I think it may be possible for new methods to dispatch on the 2nd argument as `::Symbol`, so it could be `methods(ftype, :ftype, [types], [module])`.

Could also make it a keyword-only argument `methods(Foo; mode=:ftype)` just in case adding another optional positional argument is too radical. It’s not like this argument needs to participate in multiple dispatch.

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 14, 2023, 12:12pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/18 "2023-08-14T12:12:59Z")

</div>

@Benny , thank you for the insights.

I’ll not be able to put much work into this in the following days - but before updating that PR, I would really like to somehow _validate_ this (and your idea of a standalone package where we can iterate fast seems attractive indeed).

I wrote **[this](https://discourse.julialang.org/t/community-driven-juliapowerpack-patching-pains-and-incubating-new-ideas/101988)** a while back - and this was the exact rationale: to have a fast-moving environment where we can iterate/validate ideas. Also, don’t wait for the slow-paced review process (and this is not a critique - I just admit the existing/expected constraints) before making valuable stuff available to the community.

I might revisit [that](https://discourse.julialang.org/t/community-driven-juliapowerpack-patching-pains-and-incubating-new-ideas/101988) idea.

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [August 27, 2023, 2:30pm UTC](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618/19 "2023-08-27T14:30:30Z")

</div>

After discussions [here](https://github.com/JuliaLang/julia/pull/50898), I opted for `instancemethods` flavor and arrived at the following PR:

> <https://github.com/JuliaLang/julia/pull/51068>
>
> The current PR is a follow-up to the previous (now closed) \[PR discussion\](https…://github.com/JuliaLang/julia/pull/50898).
> 
> Besides the context provided above, I still want this to be standalone, so I'll add the relevant information below (naturally, there will be an overlap with the previous PR).
> 
> \# Rationale
> 
> The inception of this PR started \[here\](https://discourse.julialang.org/t/how-to-determine-the-methods-for-a-callable-object-without-instantiating-it/102618?u=algunion) by observing that there is no easy way to determine the methods for a callable object without instantiating it.
> 
> For the following scenario, there is no way to use \`methods\` and retrieve the \`method\` list of the callable object without actually instantiating it first:
> \`\`\`julia
> struct A{T} x::T end
> (a::A)(y) = a.x+y
> \`\`\`
> Calling \`methods(A}\` will return the constructor methods. Callable object methods are only retrievable (before this PR) by performing the \`methods(A(1))\` call.
> 
> This is not practical at all (especially if the object construction is expensive). But even more, since the instances are of concrete types, extending the callable object methods in the following way will imply multiple instances construction and multiple \`methods\` calls in order to get the complete method collection defined for different \`A{T}\` types. 
> \`\`\`julia
> (a::A{Float64})(x::Float64) = a.x + x
> (a::A{String})(x::String) = a.x \* x
> 
> \# A{Float64}(1.0) instance and A{String}("s") needed
> afloat = A(1.0)
> astring = A("s")
> 
> methods(afloat)
> methods(astring) 
> \`\`\`
> 
> \# Enter \`instancemethods\`
> 
> The current PR introduces the \`instancemethods\` function. Instead of constructing an instance, the methods defined for a callable object can now be retrieved by calling \`instancemethods\` with the type of the object.
> 
> \`\`\`julia
> julia\> struct A{T}
> x::T
> end
> 
> \# making the object callable
> julia\> (a::A{Float64})(x::Float64) = a.x + x
> julia\> (a::A{String})(x::String) = a.x \* x
> 
> julia\> a = A(1.0)
> A{Float64}(1.0)
> 
> julia\> methods(a) == instancemethods(A{Float64})
> true
> 
> julia\> length(instancemethods(A{\<:Any})) == 2
> true
> \`\`\`
> 
> Additionally, getting the complete collection of methods defined for the callable object types derived from A{T} is possible by the usage of \`instancemethods(A{\<:Any})\`.
> 
> \# Potential discussion: \`instancemethods(Function)\`
> 
> Calling \`instancemethods(Function)\` will actually return all the methods defined for all the instances of \`Function\` from all modules. This returns a method list of length equal to 28184 (in my branch of 1.11.0-DEV.355). And one can also retrieve all methods relative to module/modules (e.g., \`instancemethods(Function, Base)\`. 
> 
> This \`Function\` related functionality is more like a side-effect (but it is understandable since the actual functions are instances of \`Function\`). 
> 
> At this point, I don't see a good reason to restrict the \`instancemethods(Function)\` usage - however, I want to point out that I observed the behavior, and I am open to changing this behavior if somebody provides reasons that I might miss.
> 
> \## Small wording change in \`methods\` doc
> 
> This PR also slightly changed the wording in \`methods\` docs (\*method table\* ---\> \*method list\*). The reason is obvious: \`methods\` does not return a \`Core.MethodTable\`; it actually returns \`Base.MethodList\`).
