Use of methods()

I would like to understand the use of methods(function, types). Consider the following two invocations, whose results are not the same. This implies that the order in which the types are listed in the second argument impacts the return. Is this a bug or expected behavior? Thanks.

methods(+, [Int,Float])
methods(+, [Real, Int])
methods(+, [Real,Int])
# 6 methods for generic function "+":
[1] +(x::BigInt, c::Union{Int16, Int32, Int64, Int8}) in Base.GMP at gmp.jl:527
[2] +(x::BigFloat, c::Union{Int16, Int32, Int64, Int8}) in Base.MPFR at mpfr.jl:394
[3] +(x::T, y::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at int.jl:53
[4] +(a::Integer, b::Integer) in Base at int.jl:857
[5] +(x::T, y::T) where T<:Number in Base at promotion.jl:384
[6] +(x::Number, y::Number) in Base at promotion.jl:311
methods(+, [Int,Real])
# 7 methods for generic function "+":
[1] +(x::T, y::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at int.jl:53
[2] +(c::Union{Int16, Int32, Int64, Int8}, x::BigInt) in Base.GMP at gmp.jl:528
[3] +(a::Integer, b::Integer) in Base at int.jl:857
[4] +(c::Union{Int16, Int32, Int64, Int8}, x::BigFloat) in Base.MPFR at mpfr.jl:398
[5] +(x::Integer, y::Ratios.SimpleRatio) in Ratios at /Users/erlebach/.julia/packages/Ratios/uRs4y/src/Ratios.jl:29
[6] +(x::T, y::T) where T<:Number in Base at promotion.jl:384
[7] +(x::Number, y::Number) in Base at promotion.jl:311

Here are results when I invoke methods with a single type at a time:

methods(+,[Int])
# 1 method for generic function "+":
[1] +(x::Number) in Base at operators.jl:504

methods(+,[Real])
# 3 methods for generic function "+":
[1] +(x::Bool) in Base at bool.jl:93
[2] +(x::Rational) in Base at rational.jl:247
[3] +(x::Number) in Base at operators.jl:504


methods(+,[Int])
# 1 method for generic function "+":
[1] +(x::Number) in Base at operators.jl:504

Given these results, I would expect that using two types would generate 4 results, not 6 or 7. There is clearly something I do not understand in how methods is supposed to work.

1 Like

Generally the order would of course matter — multiple dispatch does not care that + is commutative for certain domains, it may not be implemented in a way that reflects this.

1 Like

This dependence should be made clear in the documentation. It is hardly obvious. Lists of arguments are well defined, aren’t they? Could somebody please provide a MWE to demonstrate why it is obvious that order is important? The documentation implies that if I use types=[Int,Real], the methods should return all functions that have an argument that is either of type Int or of type Real.

I also do not understand why the total number of results with types=[Int] or types=[Real] adds up to less than the results using both types in a list. Perhaps this is expected behavior, but I do not understand it. There is no implication that the search only applies to the type of the first argument.

Here is a specific example. methods(+,[Int,Real]) returns a list of 7 methods, including

[3] +(a::Integer, b::Integer) in Base at int.jl:857

My question is: why isn’t this method returned when invoking methods(+,[Int])?

I understand that this might be expected behavior, but I do not understand the behavior. Thanks.

I think you may be misinterpreting what methods does.

methods(+, [Integer, Real]) returns all methods whose first argument is an Integer and whose second argument is a Real.

Lets assume everyone implements methods symmetricly, which as Tamas noted is required because Julia doesn’t know that + is commutative.

Since there are more Real types than integer types, the number of returns is num_integer_types * num_real_types, which is not N^2.

There could also be weird performance reasons that something like BigInt + Float64 is defined in a particular way but not Float64 + BigInt. I don’t know if that’s true but I could imagine that scenario.

Also note that

julia> +(4)
4

So someone has defined one-argument methods for + probably for some convenience reason.

2 Likes

Thank you, that makes sense. Note that if you invoke help in REPL, one gets


methods(f, [types], [module])

Return the method table for f.

If types is specified, return an array of methods whose types match. If module is specified, return an array of methods defined in that module. A list of modules can also be specified as an array.

There is no implication that types refer to a list of arguments in the same order as in the method definitions. Perhaps the definition could be amended?

Here is a MWE to test out some assumptions:

function Gordon(a::Real)
println(a)
end

methods(Gordon)
methods(Gordon, types=[Real])

The two invocations of methods return:

methods(Gordon)

# 1 method for generic function "Gordon":
[1] Gordon(a::Real) in Main at /Users/erlebach/covid_modeling_julia/julia_code/sir-julia/SIRsims/gordon_julia_code/household_workplaces_leon/xxx.jl:395

as expected, and


methods(Gordon, types=[Real])
ERROR: MethodError: no method matching methods(::typeof(Gordon); types=DataType[Real])
Closest candidates are:
methods(::Any) at reflection.jl:905 got unsupported keyword argument "types"
methods(::Any, ::Union{Nothing, Module, AbstractArray{Module,N} where N}) at reflection.jl:905 got unsupported keyword argument "types"
methods(::Any, ::Any) at reflection.jl:879 got unsupported keyword argument "types"
...
Stacktrace:
[1] top-level scope at none:0

which is not expected. This is probably the simplest case I can think of. In this case, my function is in the Global space and not within a Module. Should that make a difference? Thank you.

julia> methods(Gordon, [Real])
# 1 method for generic function "Gordon":
[1] Gordon(a::Real) in Main at REPL[1]:1

To edit a specific method, type the corresponding number into the REPL and press Ctrl+Q

No where in the doc it mentions that types can be a keyword argument. This is not following the python convention.

You can see from the error that it is complaining about methods which has nothing to do with your function.

3 Likes

I have to agree with @erlebach in one thing, the documentation below is incredibly vague and unhelpful. Someone can only guess what it means, and then check by experiment.

Gordon Erlebacher
Chair, Department of Scientific Computing

Here is the source code for

methods()

. There is definitely a type argument, notwithstanding the implications of the error message.

“”" methods(f, [types], [module])
the method table for f.
If types is specified, return an array of methods whose types match.
If module is specified, return an array of methods defined in that module.
A list of modules can also be specified as an array.
!!! compat “Julia 1.4”
At least Julia 1.4 is required for specifying a module.
“”"
function methods(@nospecialize(f), @nospecialize(t),
@nospecialize(mod::Union{Module,AbstractArray{Module},Nothing}=nothing))
if mod isa Module
mod = (mod,)
end
if isa(f, Core.Builtin)
throw(ArgumentError(“argument is not a generic function”))
end
t = to_tuple_type(t)
world = typemax(UInt)
MethodList(Method[m[3] for m in _methods(f, t, -1, world) if mod === nothing || m[3].module in mod],
typeof(f).name.mt)
end

This is not python, having an argument named a does not mean you can call the function with a=.... It’s also not named types FWIW…

I appreciate all the replies. The command

methods(Gordon, [Real])

works perfectly well. I must have had a typo, and no doubt, I used types= in a past thread, which was most assuredly, incorrect. Thank you for your patience with me.

1 Like