What are the limitations of completion in the REPL?

The manual states “Tab completion can also help completing fields:”, using the example of UUIDs.
However, I recently found that some symbols are not completed (Where is sparse ldlt? - #2 by skleinbo).

So, does anyone know what are the limitations and how does the completion work (or does not?)?

REPL completion works by looking up what symbols are associated with the object in front of the dot. If a symbol is not part of those associated symbols, completion can’t know about it on its own. In your example, SuiteSparse is extending a function from another module, in this case LinearAlgebra, which means the ldlt symbol is not associated with the module SuiteSparse in the first place, resulting in there not being completion for it.

It’s also not trivial to just make that available anyway, since ldlt doesn’t exist from the POV of the SuiteSparse module in the first place. Writing SuiteSparse.ldlt would have to reference LinearAlgebra.ldlt, which seems very confusing to me.

So are you saying that the completion can only look up functions, not methods?

No, not exactly. Both the function and the method live in LinearAlgebra, the text resulting in the method is just “physically” written in SuiteSparse. REPL completion can look up things that actually end up as part of SuiteSparse, which the method defined for ldlt doesn’t.

How did you come to that conclusion? I can see ldlt in SuiteSparse.jl/cholmod.jl at f31dc2e45c9edd2f9969c336a440c9aca13d6e44 · JuliaSparse/SuiteSparse.jl · GitHub

It’s imported here:

which means that the ldlt you link extends that function with a new method instead of creating an entirely different function.

You can also run SuiteSparse.ldlt === LinearAlgebra.ldlt to check that they’re the same.

Right, but still, the method is defined in CHOLMOD. Is that not visible to the completion mechanism?

No, REPL completion doesn’t know (or care) where things are written in a file, only whether they’re part of the resulting module or not. Files in general are not nearly as important in julia as in other languages like python - they’re not coupled to the module structure or what’s reachable from them in code per se.

I understand that (I think), but the method is defined in a module. Shouldn’t it be visible in the module then?

Okay, here is an example:

julia> include("m1.jl")                                                                                                                                               
julia> include("m2.jl")                                                                                                                                               
julia> Main.m2.                                                                                                                                                       
eval    f        include                                                                                                                                              
julia> Main.m2.f                                                                                                                                                      
f (generic function with 2 methods)                                                                                                                                   
julia> Main.m1.                                                                                                                                                       
eval    f        include                                                                                                                                              
julia> Main.m1.f                                                                                                                                                      
f (generic function with 2 methods)                                                                                                                                   
shell> cat m1.jl m2.jl                                                                                                                                                
module m1                                                                                                                                                             
export f                                                                                                                                                              
function f(x::Float64)                                                                                                                                                
module m2                                                                                                                                                             
import ..m1: f                                                                                                                                                        
export f                                                                                                                                                              
function f(x::Int64)                                                                                                                                                  

As you can see, completion suggests function methods for f from both module m1 and module m2, even though m2 only defines a method for f. Isn’t that the same situation as for ldlt?

Edit: With a submodule.

julia> include("m3.jl")                                                                                                                                               
WARNING: replacing module m3.                                                                                                                                         
julia> Main.m3.                                                                                                                                                       
eval    include  subm3                                                                                                                                                
julia> Main.m3.subm3.                                                                                                                                                 
eval    f        include                                                                                                                                              
julia> Main.m3.subm3.f                                                                                                                                                
f (generic function with 2 methods)                                                                                                                                   
shell> cat m3.jl                                                                                                                                                      
module m3                                                                                                                                                             
module subm3                                                                                                                                                          
import ...m1: f                                                                                                                                                       
export f                                                                                                                                                              
function f(x::Int64)                                                                                                                                                  

This is where the distinction between “where it’s written” and “where it’s associated with/where the method belongs” lies. It’s written in that file, yes, but that doesn’t necessarily mean that it semantically belongs to that module. It’s similar to when you do

module B
   # empty module

# Here B.C doesn't work

module A
    using ..B

    @eval B const C = 1

# After this, B.C works

You wouldn’t expect A.C to work, right? The same goes for ldlt of SuiteSparse.

No, because in you’re example you’re reexporting f from m2, which associates the name with m2 as well (and m1 of course already has f associated with it). This is not the case for SuiteSparse - the method is not reexported (the reason for which is possibly due to the method only being added conditionally).

The same goes for your submodule example. The submodule subm3 reexports f (which happens independently of being placed in m3) and thus has the name available, while m3 itself does not. It doesn’t know about any f (function or methods) at all - it only knows about the name subm3.


Of course, the export is the key. You are right.