Fixing the Piping/Chaining Issue

Oops! It appears that calling methodswith on a parameterized type doesn’t give the methods of the unparameterized type! My autocomplete was failing to find any methods of Vector because it was being parameterized with the type of its contents.

See this example.

julia> struct MyThing{T} end

julia> foo(::MyThing) = 1
foo (generic function with 1 method)

julia> methodswith(MyThing)
[1] foo(::MyThing) in Main at REPL[157]:1

julia> methodswith(MyThing{1})


julia> bar(::MyThing{1}) = 2
bar (generic function with 1 method)

julia> methodswith(MyThing)
[1] foo(::MyThing) in Main at REPL[157]:1

julia> methodswith(MyThing{1})
[1] bar(::MyThing{1}) in Main at REPL[160]:1

As a result, because an object such as [(a=1,)] is of type Vector{NamedTuple{(:a,), Tuple{Int64}}}, none of the methods of Vector appear.

I’ve rewritten it so it’ll detect if there are no methods of the parameterized type, and if so, give methods of the unparameterized type (with special case handling for vectors). I think it’ll have poor behavior in some particular case, but for a quick hack I think it’s not too bad. Might consider doing something more sophisticated later. find methods of the parameterized type, and append methods of the unparameterized type with special case handling for vectors and matrices.

Here's the code now.

function propose_method_autocompletions(obj, func_name_fragment::String=""; 
    type_depth = 1:typemax(Int), only_same_module = false, only_imported_modules = false,
    arg_order_inference = false, github_inference = false, personalized_inference = false)::Vector{Method}

    @assert first(type_depth) ≥ 1 && last(type_depth) ≥ 1
    recs = Vector{Method}[]
    get_type_at_depth(type, depth=1) = depth ≤ 1 ? type : type == Any ? nothing : get_type_at_depth(supertype(type), depth-1)

    for i ∈ type_depth
        stype = get_type_at_depth(typeof(obj), i)
        isnothing(stype) && break

        meths = methodswith(stype)
        if !(stype isa UnionAll) && length(stype.parameters) > 0
            if stype <: AbstractVector # special case handling for vectors
                meths = [meths; methodswith(getfield(parentmodule(stype), nameof(stype)){T,1} where T)]
            elseif stype <: AbstractMatrix
                meths = [meths; methodswith(getfield(parentmodule(stype), nameof(stype)){T,2} where T)]
            end
            meths = [meths; methodswith(getfield(parentmodule(stype), nameof(stype)))]
        end

        meths = filter(meths) do m
            length(func_name_fragment) > length(string(m.name)) && return false
            string(m.name)[1:length(func_name_fragment)] == func_name_fragment || return false
            only_same_module && (parentmodule(typeof(obj)) == m.module || return false)
            # How to detect only modules that have been explicitly imported?
            true
        end

        arg_order_inference || true # do cool sorting stuff, add later
        github_inference || true # do cool sorting stuff, add later
        personalized_inference || true # do cool sorting stuff, add later

        recs = [recs; meths]
    end

    recs
end

Calling it on the object of [(a=1,)]:


julia> propose_method_autocompletions([(a=1,)])
[1] CapturedException(ex, bt_raw::Vector) in Base at task.jl:12
[2] \(A::LinearAlgebra.Transpose{<:Complex, <:LinearAlgebra.Hermitian{<:Complex, <:SparseArrays.AbstractSparseMatrixCSC}}, B::Vector) in SparseArrays at C:\Users\unime\AppData\Local\Programs\Julia-1.8.0\share\julia\stdlib\v1.8\SparseArrays\src\linalg.jl:872
[3] \(L::SuiteSparse.CHOLMOD.FactorComponent, b::Vector) in SuiteSparse.CHOLMOD at C:\Users\unime\AppData\Local\Programs\Julia-1.8.0\share\julia\stdlib\v1.8\SuiteSparse\src\cholmod.jl:1521
[4] append!(a::Vector, items::AbstractVector) in Base at array.jl:1105
[5] deleteat!(a::Vector, i::Integer) in Base at array.jl:1485
[6] deleteat!(a::Vector, r::AbstractUnitRange{<:Integer}) in Base at array.jl:1491
[7] deleteat!(a::Vector, inds::AbstractVector{Bool}) in Base at array.jl:1590
[8] deleteat!(a::Vector, inds::AbstractVector) in Base at array.jl:1533
[9] deleteat!(a::Vector, inds) in Base at array.jl:1532
[10] empty!(a::Vector) in Base at array.jl:1738

⋮

[3209] groupview(f, X; restype, kwargs...) in FlexiGroups at C:\Users\unime\.julia\packages\FlexiGroups\1ItB2\src\base.jl:41

Beyond that, for truly generic methods that don’t specialize on anything, whose behavior can be thought of as really generalizable across any object, they shouldn’t float to the top of such a simple autocomplete anyway; if a method is general enough not to specialize on any type, it’s probably general enough to have memorized.

It’s possible to make them float to the top anyway, using a sorting technique based on statistical inference of a model fitted to a codebase (imagine if you could train your autocomplete to your own codebase!), but I’m not going to make that at the moment.

Without such inference built-in, for methods to gain visibility, they need to be specialized to such types as AbstractArray, AbstractDict, or etc.

3 Likes