I was wondering if anybody has thought about this before. Sometimes it can be hard to keep track of what methods are available for a given function and how they’re hierarchically organized. For example, the +
function has around 200 methods in a fresh Julia session. So far, methods(+)
will print a list of unordered methods. I thought, why not exploit the tree structure of the type signatures to group methods.
Here’s some preliminary code that does this and prints the structure via AbstractTrees.jl. Would something like this be useful to have by default in Julia (or is there already something I missed)?
using AbstractTrees
mutable struct Node
sig::Type
children::Vector{Node}
end
function Base.show(io::IO, n::Node)
s = replace(string(n.sig), r"^Tuple" => "")
print(io, s == "" ? "root" : s)
end
AbstractTrees.children(n::Node) = n.children
function Node(method)
s = method.sig
function skipfirst(x)
if typeof(x) === UnionAll
UnionAll(x.var, skipfirst(x.body))
else
Tuple{x.parameters[2:end]...}
end
end
Node(skipfirst(s), [])
end
function add_to_tree!(node, x)
@assert x.sig <: node.sig
added_to_child = false
for child in node.children
if x.sig <: child.sig
add_to_tree!(child, x)
added_to_child = true
end
end
if !added_to_child
for i in length(node.children):-1:1
child = node.children[i]
if child.sig <: x.sig
deleteat!(node.children, i)
add_to_tree!(x, child)
end
end
push!(node.children, x)
end
return node
end
function methodtree(func::Function)
ms = methods(func)
root = Node(Tuple{Vararg{Any}}, [])
for m in ms
add_to_tree!(root, Node(m))
end
return root
end
And here’s an example printout for push!
just because it’s not such a wall of text as +
is:
print_tree(methodtree(push!))
root
├─ {AbstractChannel, Any}
├─ {Base.IdSet, Any}
├─ {Base.InvasiveLinkedList{Base.LinkedListItem{T}}, T} where T
├─ {Base.InvasiveLinkedList{T}, T} where T
├─ {Base.InvasiveLinkedListSynchronized{T}, T} where T
├─ {OrderedCollections.OrderedSet, Any}
├─ {Set, Any}
├─ {Pkg.Resolve.ResolveLogEntry, Tuple{Union{Nothing, Pkg.Resolve.ResolveLogEntry}, String}}
├─ {LoweredCodeUtils.Links, Any}
├─ {Markdown.MD, Any}
├─ {Revise.PkgData, Pair{<:AbstractString, Revise.FileInfo}}
├─ {CompositeException, Any}
├─ {AbstractVector, Vararg{Any}}
│ ├─ {Vector{T}, Any} where T
│ │ └─ {Vector{Any}, Any}
│ ├─ {BitVector, Any}
│ └─ {VSCodeServer.JSON.Parser.PushVector, Any}
├─ {Distributed.AbstractWorkerPool, Int64}
│ └─ {Distributed.WorkerPool, Int64}
├─ {AbstractDict, Pair}
│ └─ {Base.EnvDict, Pair{<:AbstractString}}
├─ {BitSet, Vararg{Integer}}
│ └─ {BitSet, Integer}
├─ {Base.Nowhere, Any}
├─ {Revise.WatchList, Pair{<:AbstractString, Revise.PkgData}}
├─ {Revise.WatchList, Pair{<:AbstractString, CodeTracking.PkgFiles}}
├─ {Revise.WatchList, Pair{<:AbstractString, Base.PkgId}}
└─ {Any, Any, Any, Vararg{Any}}
├─ {Any, Any, Any}
│ └─ {Pkg.Resolve.ResolveLogEntry, Tuple{Union{Nothing, Pkg.Resolve.ResolveLogEntry}, String}, Bool}
└─ {AbstractDict, Pair, Pair, Vararg{Pair}}
└─ {AbstractDict, Pair, Pair}