The method which is called for f(; x=1) is Core.kwcall(...) as correctly given by @which. However, that method is not a method of function f but a method of function Core.kwcall, as indicated by its name, so it won’t appear in methods(f). Actually, the only method that appears in methods(f) is that which is called with f(), which throws an UndefKeywordError error: you can indeed check that:
julia> (@which f()) == only(methods(f))
true
The full details of the inner workings of keywods arguments are given in the dev docs: Julia Functions · The Julia Language
and as you can see there, whenever a method with keyword arguments is defined in the code, multiple methods are actually defined, so if you wanted to collect all the relevant methods for your example case here, there would be the one in methods(f), the Core.kwcall one you can get from @which, and a last one found as methods(var"#f#1")… however the number at the end (here 1) depends on how many keyword argument methods were defined before, and it is an implementation detail of the compiler, I don’t know if there is a proper way to retrieve that name var"#f#1".
It’s not exactly a matter of finding the method specializations but rather finding the method itself. But even though I can’t really recommend it as part of a workflow, you can always do:
julia> f(; x) = 2x
f (generic function with 1 method)
julia> f(y; x1, x2) = y*(x1 + 2x2)
f (generic function with 2 methods)
julia> kwfunctions = eval.(filter(x -> startswith(String(x), "#f#"), names(Main; all=true, imported=false)))
2-element Vector{Function}:
#f#1 (generic function with 1 method)
#f#2 (generic function with 1 method)
julia> [Base.specializations(only(methods(x))) for x in kwfunctions]
2-element Vector{Base.MethodSpecializations}:
Base.MethodSpecializations(svec())
Base.MethodSpecializations(svec())
julia> f(; x=36)
72
julia> [Base.specializations(only(methods(x))) for x in kwfunctions]
2-element Vector{Base.MethodSpecializations}:
Base.MethodSpecializations(MethodInstance for var"#f#1"(::Int64, ::typeof(f)))
Base.MethodSpecializations(svec())
julia> f(14; x1=13.6, x2=4//5)
212.79999999999998
julia> [Base.specializations(only(methods(x))) for x in kwfunctions]
2-element Vector{Base.MethodSpecializations}:
Base.MethodSpecializations(MethodInstance for var"#f#1"(::Int64, ::typeof(f)))
Base.MethodSpecializations(MethodInstance for var"#f#2"(::Float64, ::Rational{Int64}, ::typeof(f), ::Int64))
Method specializations are internal details of the language’s implementation, which are subject to changes across versions and aren’t part of the language’s standard. If you make base language API that “gets a method’s specializations”, you effectively force method specializations into the language standard and limit the implementations. This sort of reflection will at best be an unstable, implementation-specific, third-party library.
That’s right, and actually even Base.specializations is not part of the public API, as you can see from the REPL documentation:
help?> Base.specializations
│ Warning
│
│ The following bindings may be internal; they may change or be removed in future versions:
│
│ • Base.specializations
I find this kind of question very interesting to learn how the internals of the language work, but you can’t rely on the hack I showed before (or even Base.specializations thus) for production purpose.
I appreciate the caution that I’m venturing into non-public API. I was just trying to learn about methods and dispatch vs specialization, and am not relying on these internals for production.