Autocompletion and function/method discoverability

Object-oriented languages make use of the obj.fun syntax for function calls. If the user types obj. and then presses TAB, he/she can immedietly learn about the available functions.

The Julia story is more challenging, but I wonder if we could ever provide tooling to improve the discoverability of functions for beginners. I’ve asked this question on Zulip and am asking it here now for greater visibility.

Could VSCode or some other tool in the ecosystem display the most relevant methods for the obj upon typing some special character? For example, obj# and TAB to trigger a list of functions and their most relevant methods in the REPL?

Assuming that the answer to my previous question is yes, could this same tool automatically rewrite the expression into the fun(obj, ...) syntax that Julia expects positioning the obj correctly in the list of arguments?

You can do this with ?(x, tab. Or use shifttab to include all the completely untyped ::Any methods, too. It’s… not the most useful thing in the world. Perhaps it’d be slightly better if the default tab behavior only gave you non-Any answers in slot you “filled in”.

julia>  ?(r"str"#TAB
rem(x, y, ::RoundingMode{:FromZero}) @ Base div.jl:105
rem(x, y, r::RoundingMode{:Nearest}) @ Base div.jl:102
rem(x, y, ::RoundingMode{:Up}) @ Base div.jl:101
rem(x, y, ::RoundingMode{:Down}) @ Base div.jl:100
rem(x, y, ::RoundingMode{:ToZero}) @ Base div.jl:99
rem(a::T, b::Base.MultiplicativeInverses.MultiplicativeInverse{T}) where T @ Base.MultiplicativeInverses multinverses.jl:168
*(r::Regex) @ Base regex.jl:849
*(r1::Union{Regex, AbstractChar, AbstractString}, rs::Union{Regex, AbstractChar, AbstractString}...) @ Base regex.jl:828
(::Colon)(start::T, step, stop::T) where T @ Base range.jl:49
(::Colon)(start::T, stop::T) where T @ Base range.jl:7
<(::Any, ::Missing) @ Base missing.jl:85
<<(v, bi::Base.HashArrayMappedTries.BitmapIndex) @ Base.HashArrayMappedTries hamt.jl:124
==(a::Regex, b::Regex) @ Base regex.jl:786
==(::Any, ::Missing) @ Base missing.jl:76
==(w, v::WeakRef) @ Base gcutils.jl:36
>>(v, bi::Base.HashArrayMappedTries.BitmapIndex) @ Base.HashArrayMappedTries hamt.jl:125
BigFloat(x, prec::Int64) @ Base deprecated.jl:103
BigFloat(x, prec::Int64, rounding::RoundingMode) @ Base deprecated.jl:103
broadcast(f, x::Number...) @ Base.Broadcast broadcast.jl:813
broadcast(f, tvs::Union{Number, LinearAlgebra.Transpose{T, <:AbstractVector} where T}...) @ LinearAlgebra ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/LinearAlgebra/src/adjtrans.jl:411
broadcast(f, avs::Union{Number, LinearAlgebra.Adjoint{T, <:AbstractVector} where T}...) @ LinearAlgebra ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/LinearAlgebra/src/adjtrans.jl:410
broadcast(f, t::NTuple{N, Any}, ts::NTuple{N, Any}...) where N @ Base.Broadcast broadcast.jl:814
broadcast(f::Tf, As...) where Tf @ Base.Broadcast broadcast.jl:810
broadcast!(f::Tf, dest, As::Vararg{Any, N}) where {Tf, N} @ Base.Broadcast broadcast.jl:849
CapturedException(ex, processed_bt::Vector{Any}) @ Base task.jl:21
CapturedException(ex, bt_raw::Vector) @ Base task.jl:12
HTML(content::T) where T @ Base.Docs docs/utils.jl:30
Text(content::T) where T @ Base.Docs docs/utils.jl:88
hash(r::Regex, h::UInt64) @ Base regex.jl:792
first(itr, n::Integer) @ Base abstractarray.jl:502
last(itr, n::Integer) @ Base abstractarray.jl:552
transcode(T, src::String) @ Base strings/cstring.jl:178
LinRange(start, stop, len::Integer) @ Base range.jl:589
edit(f, idx::Integer) @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/InteractiveUtils/src/editless.jl:271
less(file, line::Integer) @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/InteractiveUtils/src/editless.jl:314
varinfo(pat::Regex; kwargs...) @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/InteractiveUtils/src/InteractiveUtils.jl:86
Slices(A::P, slicemap::SM, ax::AX) where {P, SM, AX} @ Base slicearray.jl:41
Some(value::T) where T @ Base some.jl:12
StepRange(start::T, step::S, stop::T) where {T, S} @ Base range.jl:384
StepRangeLen(ref::R, step::S, len::Integer, offset::Integer) where {R, S} @ Base range.jl:517
StepRangeLen(ref::R, step::S, len::Integer) where {R, S} @ Base range.jl:517
foreach(f, channel::Channel; schedule, ntasks) @ Base threads_overloads.jl:41
lock(f, wkh::WeakKeyDict) @ Base weakkeydict.jl:81
lock(f, c::Channel) @ Base channels.jl:563
lock(f, l::Base.Lockable) @ Base lock.jl:343
lock(f, l::Base.AbstractLock) @ Base lock.jl:229
lock(f, c::Base.GenericCondition) @ Base condition.jl:78
trylock(f, wkh::WeakKeyDict) @ Base weakkeydict.jl:82
trylock(f, l::Base.AbstractLock) @ Base lock.jl:238
^(r::Regex, i::Integer) @ Base regex.jl:901
accumulate(op, xs::Tuple; init) @ Base accumulate.jl:297
all(f, itr::Tuple) @ Base reduce.jl:1323
allequal(f, xs::Tuple) @ Base set.jl:659
allunique(f::F, t::Tuple) where F @ Base set.jl:598
any(f, itr::Tuple) @ Base reduce.jl:1251
asyncmap(f, b::BitArray; kwargs...) @ Base asyncmap.jl:248
asyncmap(f, s::AbstractString; kwargs...) @ Base asyncmap.jl:241
bitstring(x::T) where T @ Base intfuncs.jl:958
clamp(x, ::Type{T}) where T<:Integer @ Base intfuncs.jl:1290
clamp(x::X, lo::L, hi::H) where {X, L, H} @ Base intfuncs.jl:1266
count(f, A::Union{Base.AbstractBroadcasted, AbstractArray}; dims, init) @ Base reducedim.jl:412
count(t::Union{AbstractPattern, AbstractChar, AbstractString}, s::AbstractString; overlap) @ Base regex.jl:550
count!(f, r::AbstractArray, A::Union{Base.AbstractBroadcasted, AbstractArray}; init) @ Base reducedim.jl:446
div(a::T, b::Base.MultiplicativeInverses.UnsignedMultiplicativeInverse{T}) where T @ Base.MultiplicativeInverses multinverses.jl:162
div(a::T, b::Base.MultiplicativeInverses.SignedMultiplicativeInverse{T}) where T @ Base.MultiplicativeInverses multinverses.jl:157
divrem(x, y, ::RoundingMode{:FromZero}) @ Base div.jl:264
divrem(a::T, b::Base.MultiplicativeInverses.MultiplicativeInverse{T}) where T @ Base.MultiplicativeInverses multinverses.jl:171
divrem(a, b, r::RoundingMode) @ Base div.jl:184
eachmatch(re::Regex, str::AbstractString; overlap) @ Base regex.jl:781
error(s::Vararg{Any, N}) where N @ Base error.jl:42
evalpoly(x, p::AbstractVector) @ Base.Math math.jl:107
evalpoly(x, p::Tuple) @ Base.Math math.jl:94
extrema(f, a::AbstractArray; dims, kw...) @ Base reducedim.jl:983
fill(v, dims::Tuple{}) @ Base array.jl:538
fill(v, dims::Union{Integer, AbstractUnitRange}...) @ Base array.jl:535
fill(v, dims::NTuple{N, Integer}) where N @ Base array.jl:537
fill(v, dims::NTuple{N, Union{Integer, Base.OneTo}}) where N @ Base array.jl:536
filter!(f, s::BitSet) @ Base bitset.jl:328
filter!(f, d::IdSet) @ Base idset.jl:106
filter!(pred, h::Dict{K, V}) where {K, V} @ Base dict.jl:718
filter!(f, d::IdDict) @ Base iddict.jl:186
filter!(f, d::WeakKeyDict) @ Base weakkeydict.jl:208
filter!(f, d::AbstractDict) @ Base abstractdict.jl:437
filter!(f, a::AbstractVector) @ Base array.jl:2918
filter!(f, s::Set) @ Base set.jl:668
findall(t::Union{AbstractPattern, AbstractString, AbstractVector{<:Union{Int8, UInt8}}}, s::Union{AbstractPattern, AbstractString, AbstractVector{<:Union{Int8, UInt8}}}; overlap) @ Base strings/search.jl:464
findfirst(r::Regex, s::AbstractString) @ Base regex.jl:496
findmax(f, A::AbstractArray; dims) @ Base reducedim.jl:1198
findmin(f, A::AbstractArray; dims) @ Base reducedim.jl:1125
findnext(re::Regex, str::Union{String, SubString}, idx::Integer) @ Base regex.jl:467
findnext(r::Regex, s::AbstractString, idx::Integer) @ Base regex.jl:493
foreach(f, itr::Tuple) @ Base tuple.jl:692
foreach(f, itr::Tuple, itrs::Tuple...) @ Base tuple.jl:693
getproperty(x, f::Symbol, order::Symbol) @ Base Base.jl:70
getproperty(x, f::Symbol) @ Base Base.jl:49
hasmethod(f, t, kwnames::Tuple{Vararg{Symbol}}; world) @ Base reflection.jl:2355
hasproperty(x, s::Symbol) @ Base reflection.jl:2625
hcat(X::T...) where T @ Base abstractarray.jl:1639
in(x, s::IdSet) @ Base idset.jl:44
in(p, a::AbstractDict) @ Base abstractdict.jl:29
in(x, s::Set) @ Base set.jl:92
in(x, itr::Tuple) @ Base operators.jl:1302
in(x::T, r::AbstractRange{T}) where T @ Base range.jl:1424
in(key, v::Base.KeySet{<:Any, <:Dict}) @ Base dict.jl:549
in(k, v::Base.KeySet{<:Any, <:IdDict}) @ Base iddict.jl:182
in(k, v::Base.KeySet) @ Base abstractdict.jl:73
in!(x, s::Set) @ Base set.jl:129
in!(x, s::AbstractSet) @ Base set.jl:125
indexin(a, b::AbstractArray) @ Base array.jl:2745
insorted(x, r::AbstractRange) @ Base.Sort sort.jl:461
insorted(x, v::AbstractVector; kw...) @ Base.Sort sort.jl:460
invpermute!(v, p::AbstractVector) @ Base combinatorics.jl:237
isapprox(::Any, ::Missing; kwargs...) @ Base missing.jl:91
isequal(::Any, ::Missing) @ Base missing.jl:82
isless(::Any, ::Missing) @ Base missing.jl:88
issetequal(a, b::AbstractSet) @ Base abstractset.jl:518
issorted(itr, order::Base.Order.Ordering) @ Base.Sort sort.jl:50
lpad(s, n::Integer, p::Union{AbstractChar, AbstractString}) @ Base strings/util.jl:467
lpad(s, n::Integer) @ Base strings/util.jl:467
lstrip(f, s::AbstractString) @ Base strings/util.jl:375
map!(f, iter::Base.ValueIterator{<:WeakKeyDict}) @ Base weakkeydict.jl:132
map!(f, iter::Base.ValueIterator{<:Dict}) @ Base dict.jl:734
map!(f::F, dest::AbstractArray, A::AbstractArray) where F @ Base abstractarray.jl:3362
map!(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) where F @ Base abstractarray.jl:3405
map!(f::F, dest::AbstractArray, As::AbstractArray...) where F @ Base abstractarray.jl:3471
map!(f, iter::Base.ValueIterator) @ Base abstractdict.jl:671
mapreduce(f, op, A::Union{Base.AbstractBroadcasted, AbstractArray}; dims, init) @ Base reducedim.jl:329
mapreduce(f, op, A::Union{Base.AbstractBroadcasted, AbstractArray}...; kw...) @ Base reducedim.jl:331
mapreduce(f, op, itr::Base.SkipMissing{<:AbstractArray}) @ Base missing.jl:273
mapreduce(f, op, a::Number) @ Base reduce.jl:448
mapslices(f, A::AbstractArray; dims) @ Base abstractarray.jl:3265
match(re::Regex, str::Base.AnnotatedString, idx::Integer) @ Base regex.jl:455
match(re::Regex, str::Union{SubString{String}, String}, idx::Integer) @ Base regex.jl:415
match(r::Regex, s::AbstractString, i::Integer) @ Base regex.jl:463
match(re::Regex, str::Base.AnnotatedString) @ Base regex.jl:448
match(r::Regex, s::AbstractString) @ Base regex.jl:462
match(re::Regex, str::Base.AnnotatedString, idx::Integer, add_opts::UInt32) @ Base regex.jl:455
match(re::Regex, str::Union{SubString{String}, String}, idx::Integer, add_opts::UInt32) @ Base regex.jl:415
max(::Any, ::Missing) @ Base missing.jl:137
maximum(f, a::AbstractArray; dims, kw...) @ Base reducedim.jl:983
mergewith(combine, d::AbstractDict, others::AbstractDict...) @ Base abstractdict.jl:401
mergewith!(combine, d1::Dict{K, V}, d2::AbstractDict) where {K, V} @ Base dict.jl:746
mergewith!(combine, d1::AbstractDict, d2::AbstractDict) @ Base abstractdict.jl:286
mergewith!(combine, d::AbstractDict, others::AbstractDict...) @ Base abstractdict.jl:282
methods(f, t, mod::Module) @ Base reflection.jl:1230
methods(f, mod::Union{Nothing, Module, AbstractArray{Module}}) @ Base reflection.jl:1242
methods(f, t, mod::Union{Nothing, Tuple{Module}, AbstractArray{Module}}) @ Base reflection.jl:1218
min(::Any, ::Missing) @ Base missing.jl:134
minimum(f, a::AbstractArray; dims, kw...) @ Base reducedim.jl:983
modifyproperty!(x, f::Symbol, op, v, order::Symbol) @ Base Base.jl:84
modifyproperty!(x, f::Symbol, op, v) @ Base Base.jl:84
ntuple(f, ::Val{3}) @ Base ntuple.jl:50
ntuple(f, ::Val{2}) @ Base ntuple.jl:49
ntuple(f, ::Val{1}) @ Base ntuple.jl:48
ntuple(f, ::Val{0}) @ Base ntuple.jl:47
ntuple(f::F, ::Val{N}) where {F, N} @ Base ntuple.jl:69
ntuple(f::F, n::Integer) where F @ Base ntuple.jl:17
occursin(r::Regex, s::SubString{String}; offset) @ Base regex.jl:303
occursin(r::Regex, s::AbstractString; offset) @ Base regex.jl:298
oneunit(x::T) where T @ Base number.jl:371
permute!(v, p::AbstractVector) @ Base combinatorics.jl:208
permutedims!(dest, src::AbstractArray, perm) @ Base.PermutedDimsArrays permuteddimsarray.jl:284
precompile(f, argtypes::Tuple, m::Method) @ Base loading.jl:4039
precompile(f, argtypes::Tuple) @ Base loading.jl:4015
prod(f, a::AbstractArray; dims, kw...) @ Base reducedim.jl:983
promote(x::T, y::T, zs::T...) where T @ Base promotion.jl:416
propertynames(x, private::Bool) @ Base reflection.jl:2612
rand(X, d::Integer, dims::Integer...) @ Random ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/Random/src/Random.jl:285
rand(X, dims::NTuple{N, Int64} where N) @ Random ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/Random/src/Random.jl:282
range(start, stop, length::Integer) @ Base range.jl:151
read(stream, ::Type{Union{}}, slurp...; kwargs...) @ Base io.jl:236
reduce(op, A::AbstractArray; kw...) @ Base reducedim.jl:378
reduce(op, a::Number) @ Base reduce.jl:489
replace(A, old_new::Pair...; count) @ Base set.jl:815
replace!(A, old_new::Pair...; count) @ Base set.jl:735
replaceproperty!(x, f::Symbol, expected, desired, success_order::Symbol, fail_order::Symbol) @ Base Base.jl:88
replaceproperty!(x, f::Symbol, expected, desired, success_order::Symbol) @ Base Base.jl:88
replaceproperty!(x, f::Symbol, expected, desired) @ Base Base.jl:88
rpad(s, n::Integer, p::Union{AbstractChar, AbstractString}) @ Base strings/util.jl:499
rpad(s, n::Integer) @ Base strings/util.jl:499
rstrip(f, s::AbstractString) @ Base strings/util.jl:410
setproperty!(x, f::Symbol, v, order::Symbol) @ Base Base.jl:71
setproperty!(x, f::Symbol, v) @ Base Base.jl:50
setpropertyonce!(x, f::Symbol, desired, success_order::Symbol, fail_order::Symbol) @ Base Base.jl:94
setpropertyonce!(x, f::Symbol, desired, success_order::Symbol) @ Base Base.jl:94
setpropertyonce!(x, f::Symbol, desired) @ Base Base.jl:94
skipchars(predicate, io::IO; linecomment) @ Base io.jl:1468
strip(f, s::AbstractString) @ Base strings/util.jl:448
sum(f, a::AbstractArray; dims, kw...) @ Base reducedim.jl:983
swapproperty!(x, f::Symbol, v, order::Symbol) @ Base Base.jl:78
swapproperty!(x, f::Symbol, v) @ Base Base.jl:78
systemerror(p, errno::Int32; extrainfo) @ Base error.jl:176
systemerror(p, b::Bool; extrainfo) @ Base error.jl:175
timedwait(testcb, timeout::Real; pollint) @ Base asyncevent.jl:356
unique!(f, A::AbstractVector; seen) @ Base set.jl:373
vcat(X::T...) where T @ Base abstractarray.jl:1637
widen(x::T) where T @ Base operators.jl:900
withenv(f, keyvals::Pair{T}...) where T<:AbstractString @ Base env.jl:259
⊊(a, b::AbstractSet) @ Base abstractset.jl:417
MethodError(f, args, world::UInt64) @ Core boot.jl:396
Task(f, reserved_stack::Int64) @ Base task.jl:5
TypeError(where, expected::Type, got) @ Core boot.jl:371
TypeError(func, context, expected::Type, got) @ Core boot.jl:368
VecElement(arg::T) where T @ Core boot.jl:453
1 Like

Not exactly. Given a statically known class of said obj, we can discover the methods encapsulated by that class (this might be generalized to some other ways of organizing functions under a classification of the instance). We do not get all the functions that take the object as another argument, like outerfun(obj) or otherclassobj.fun2(obj), which is fair because those calls are not the calls that start with obj. and can be an awkwardly large set, even if we restrict it to explicit annotations of the exact type of obj. Julia just happens to be all outerfun calls, so maybe we can make do with a methodswith call prioritizes the type’s parent module. Not sure if we want supertypes=false or supertypes=true (excludes Any) on that.

They seem to both include the Any methods for me…kind of weird because methodswith doesn’t do that.

There is a difference, but it’s hard to see. I do think it should probably behave like methodswith in the default (non-shift) case.

julia> ?(r"str", "s", 2#TAB

broadcast(f::Tf, As...) where Tf @ Base.Broadcast broadcast.jl:810
broadcast!(f::Tf, dest, As::Vararg{Any, N}) where {Tf, N} @ Base.Broadcast broadcast.jl:849
LinRange(start, stop, len::Integer) @ Base range.jl:589
Slices(A::P, slicemap::SM, ax::AX) where {P, SM, AX} @ Base slicearray.jl:41
StepRangeLen(ref::R, step::S, len::Integer, offset::Integer) where {R, S} @ Base range.jl:517
StepRangeLen(ref::R, step::S, len::Integer) where {R, S} @ Base range.jl:517
clamp(x::X, lo::L, hi::H) where {X, L, H} @ Base intfuncs.jl:1266
error(s::Vararg{Any, N}) where N @ Base error.jl:42
findnext(re::Regex, str::Union{String, SubString}, idx::Integer) @ Base regex.jl:467
mapreduce(f, op, a::Number) @ Base reduce.jl:448
match(re::Regex, str::Union{SubString{String}, String}, idx::Integer) @ Base regex.jl:415
match(re::Regex, str::Union{SubString{String}, String}, idx::Integer, add_opts::UInt32) @ Base regex.jl:415
range(start, stop, length::Integer) @ Base range.jl:151
julia> ?(r"str", "s", 2#SHIFT-TAB

&(a, b, c, xs...) @ Base operators.jl:596
*(a, b, c, xs...) @ Base operators.jl:596
+(a, b, c, xs...) @ Base operators.jl:596
<:(...) @ Core none:0
broadcast(f::Tf, As...) where Tf @ Base.Broadcast broadcast.jl:810
broadcast!(f::Tf, dest, As::Vararg{Any, N}) where {Tf, N} @ Base.Broadcast broadcast.jl:849
HTML(xs...) @ Base.Docs docs/utils.jl:33
print(xs...) @ Base coreio.jl:3
ExponentialBackOff(n, first_delay, max_delay, factor, jitter) @ Base error.jl:249
intersect(itr, itrs...) @ Base array.jl:3061
map(f, arg, args...) @ Base.Iterators iterators.jl:62
zip(a...) @ Base.Iterators iterators.jl:374
LazyString(args...) @ Base strings/lazy.jl:41
LinRange(start, stop, len::Integer) @ Base range.jl:589
code_llvm(args...; kwargs...) @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/InteractiveUtils/src/codeview.jl:291
code_native(args...; kwargs...) @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/InteractiveUtils/src/codeview.jl:318
code_warntype(args...; kwargs...) @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.aarch64.apple.darwin14/share/julia/stdlib/v1.11/InteractiveUtils/src/codeview.jl:159
Slices(A::P, slicemap::SM, ax::AX) where {P, SM, AX} @ Base slicearray.jl:41
StepRangeLen(ref::R, step::S, len::Integer, offset::Integer) where {R, S} @ Base range.jl:517
StepRangeLen(ref::R, step::S, len::Integer) where {R, S} @ Base range.jl:517
accumulate!(op, B, A; dims, kw...) @ Base accumulate.jl:348
asyncmap(f, c...; ntasks, batch_size) @ Base asyncmap.jl:74
asyncmap!(f, r, c1, c...; ntasks, batch_size) @ Base asyncmap.jl:400
cat(A...; dims) @ Base abstractarray.jl:2084
IntrinsicFunction(...) @ Core none:0
clamp(x::X, lo::L, hi::H) where {X, L, H} @ Base intfuncs.jl:1266
cmp(<, x, y) @ Base operators.jl:460
coalesce(x, y...) @ Base missing.jl:422
ctime(path...) @ Base.Filesystem stat.jl:494
eachindex(itrs...) @ Base abstractarray.jl:318
error(s::Vararg{Any, N}) where N @ Base error.jl:42
filemode(path...) @ Base.Filesystem stat.jl:494
filesize(path...) @ Base.Filesystem stat.jl:494
findnext(re::Regex, str::Union{String, SubString}, idx::Integer) @ Base regex.jl:467
foreach(f, itr, itrs...) @ Base abstractarray.jl:3188
gperm(path...) @ Base.Filesystem stat.jl:494
hcat(X...) @ Base abstractarray.jl:1968
invokelatest(f, args...; kwargs...) @ Base essentials.jl:1052
isblockdev(path...) @ Base.Filesystem stat.jl:494
ischardev(path...) @ Base.Filesystem stat.jl:494
isdir(path...) @ Base.Filesystem stat.jl:494
isfifo(path...) @ Base.Filesystem stat.jl:494
isfile(path...) @ Base.Filesystem stat.jl:494
islink(path...) @ Base.Filesystem stat.jl:497
ismount(path...) @ Base.Filesystem stat.jl:516
ispath(path...) @ Base.Filesystem stat.jl:494
issetgid(path...) @ Base.Filesystem stat.jl:494
issetuid(path...) @ Base.Filesystem stat.jl:494
issocket(path...) @ Base.Filesystem stat.jl:494
issticky(path...) @ Base.Filesystem stat.jl:494
join(iterator, delim, last) @ Base strings/io.jl:378
kron(a, b, c, xs...) @ Base operators.jl:596
lstat(path...) @ Base.Filesystem stat.jl:226
map(f, it, iters...) @ Base abstractarray.jl:3502
mapfoldl(f, op, itr; init) @ Base reduce.jl:175
mapfoldr(f, op, itr; init) @ Base reduce.jl:223
mapreduce(f, op, a::Number) @ Base reduce.jl:448
mapreduce(f, op, itrs...; kw...) @ Base reduce.jl:308
match(re::Regex, str::Union{SubString{String}, String}, idx::Integer) @ Base regex.jl:415
match(re::Regex, str::Union{SubString{String}, String}, idx::Integer, add_opts::UInt32) @ Base regex.jl:415
max(a, b, c, xs...) @ Base operators.jl:596
min(a, b, c, xs...) @ Base operators.jl:596
mtime(path...) @ Base.Filesystem stat.jl:494
muladd(x, y, z) @ Base.Math math.jl:1470
nand(x...) @ Base bool.jl:107
nor(x...) @ Base bool.jl:145
operm(path...) @ Base.Filesystem stat.jl:494
pipeline(a, b, c, d...) @ Base cmd.jl:427
println(xs...) @ Base coreio.jl:4
printstyled(msg...; bold, italic, underline, blink, reverse, hidden, color) @ Base util.jl:143
promote(x, y, z) @ Base promotion.jl:404
promote(x, y, z, a...) @ Base promotion.jl:410
promote_type(T, S, U) @ Base promotion.jl:303
promote_type(T, S, U, V...) @ Base promotion.jl:304
push!(A, a, b) @ Base abstractarray.jl:3536
push!(A, a, b, c...) @ Base abstractarray.jl:3537
pushfirst!(A, a, b) @ Base abstractarray.jl:3538
pushfirst!(A, a, b, c...) @ Base abstractarray.jl:3539
range(start, stop, length::Integer) @ Base range.jl:151
setdiff(itr, itrs...) @ Base array.jl:3062
something(x, y...) @ Base some.jl:104
stack(f, xs, yzs...; dims) @ Base abstractarray.jl:2888
stat(path...) @ Base.Filesystem stat.jl:216
string(xs...) @ Base strings/io.jl:189
symdiff(s, sets...) @ Base abstractset.jl:272
typejoin(t, s, u) @ Base promotion.jl:22
typejoin(t, s, u, ts...) @ Base promotion.jl:23
union(s, sets...) @ Base abstractset.jl:57
uperm(path...) @ Base.Filesystem stat.jl:494
vcat(X...) @ Base abstractarray.jl:1918
xor(a, b, c, xs...) @ Base operators.jl:596
|(a, b, c, xs...) @ Base operators.jl:596
∘(f, g, h...) @ Base operators.jl:1063
≉(args...; kws...) @ Base floatfuncs.jl:259
===(...) @ Core none:0
Expr(args...) @ Core boot.jl:271
LoadError(file, line, error) @ Core boot.jl:408
Symbol(x...) @ Base strings/basic.jl:229
TypeVar(n, lb, ub) @ Core boot.jl:298
applicable(...) @ Core none:0
fieldtype(...) @ Core none:0
finalizer(...) @ Core none:0
getfield(...) @ Core none:0
getglobal(...) @ Core none:0
ifelse(...) @ Core none:0
invoke(...) @ Core none:0
isa(...) @ Core none:0
isdefined(...) @ Core none:0
modifyfield!(...) @ Core none:0
modifyglobal!(...) @ Core none:0
nfields(...) @ Core none:0
print(a...) @ Core boot.jl:695
println(a...) @ Core boot.jl:696
replacefield!(...) @ Core none:0
replaceglobal!(...) @ Core none:0
setfield!(...) @ Core none:0
setfieldonce!(...) @ Core none:0
setglobal!(...) @ Core none:0
setglobalonce!(...) @ Core none:0
sizeof(...) @ Core none:0
swapfield!(...) @ Core none:0
swapglobal!(...) @ Core none:0
throw(...) @ Core none:0
tuple(...) @ Core none:0
typeassert(...) @ Core none:0
typeof(...) @ Core none:0

I think you misunderstood my question. Say I have an instance of a Diagonal matrix:

julia> using LinearAlgebra

julia> A = Diagonal([1,2,3])
3×3 Diagonal{Int64, Vector{Int64}}:
 1  ⋅  ⋅
 ⋅  2  ⋅
 ⋅  ⋅  3

and that I am curious to learn what I can do with it.

I tried pressing ?A#TAB but that only showed a bunch of functions and variables starting with the character A.

You’re doing name autocomplete in help mode. Stay in REPL mode, and the parentheses is not optional: <Space>?(A<Tab>

There is a fair bit of Diagonal in that list but it’s probably not the neat list anybody really wants. Unfortunately methodswidth doesn’t help much here. If you try the exact type of A, obviously no method was implemented for it, but if you try the modest Diagonal, there’s hundreds:

julia> length(methodswith(typeof(A)))
0

julia> length(methodswith(Diagonal))
211

Trimming the list only goes so far, there are still many callable names:

julia> unique([m.name for m in methodswith(Diagonal)]) |> length
88
1 Like

Got it. That definitely isn’t user-friendly.

I am thinking of someone using the REPL and exploring the methods with TAB. So ideally that user would just type something after the expression and press a trigger. A. and then TAB or A# and then TAB, …

After learning the options, the user can press a single BACKSPACE to continue with normal coding. Or in an ideal situation, the user could select the desired method from a menu and get the final expression rewritten by the tool in functional form.

obj.fun doesn’t start typical calls, so the trigger would just be obj. But we wouldn’t want to trigger it with just TAB.

obj. does start property syntax, and TAB after that already looks for encapsulated names. That’s just much less useful when we don’t have classes encapsulating important methods.

julia> (1im).#TAB
im
re

julia> module B
         c=1
         d=2
       end
Main.B

julia> B.#TAB
c
d
eval
include

An opportunity to simplify the printout would be to collapse the following into fewer pseudo-signatures (maybe by arity?) emphasizing our queried type. Obviously I would also want this in a normal reflection method call, not just an interactive keyboard shortcut.

[172] ldiv!(C::UpperTriangular, D::Diagonal, A::UnitUpperTriangular) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:615
[173] ldiv!(C::UpperTriangular, D::Diagonal, A::UpperTriangular) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:614
[174] ldiv!(C::LowerTriangular, D::Diagonal, A::UnitLowerTriangular) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:615
[175] ldiv!(C::LowerTriangular, D::Diagonal, A::LowerTriangular) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:614
[176] ldiv!(T::Tridiagonal, D::Diagonal, S::Union{SymTridiagonal, Tridiagonal}) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:527
[177] ldiv!(D::Diagonal, A::UnitLowerTriangular) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:611
[178] ldiv!(D::Diagonal, A::LowerTriangular) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:610
[179] ldiv!(D::Diagonal, A::UnitUpperTriangular) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:611
[180] ldiv!(D::Diagonal, A::UpperTriangular) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:610
[181] ldiv!(Dc::Diagonal, Da::Diagonal, Db::Diagonal) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:509
[182] ldiv!(D::Diagonal, B::AbstractVecOrMat) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:481
[183] ldiv!(B::AbstractVecOrMat, D::Diagonal, A::AbstractVecOrMat) @ LinearAlgebra C:\Users\benm1\.julia\juliaup\julia-1.11.6+0.x64.w64.mingw32\share\julia\stdlib\v1.11\LinearAlgebra\src\diagonal.jl:482

Any character would do it. Doesn’t need to be . It could be # or & or any other character not taken yet for autocompletion.

Maybe spaceless ?<TAB>, that’s already invalid to the parser and intuitive for a keyboard shortcut query.

1 Like

Something like that could make a huge difference.

My personal experience from training courses is that most participants struggle finding functions even when the packages have very extensive documentation. They wish they had a simple TAB like they have in other languages.

Unfortunately TAB suggestions just aren’t that good at discovering functionality. Again, it only finds class-encapsulated methods (or whatever other sort of encapsulation), which is often not the interesting part (tab-completing a NumPy array never found anything in SciPy, if it’s even loaded). On the other hand, the set of all callable functions is overwhelmingly large, and only a subset actually works, which we can’t check quickly. It’s good documentation (not just a massive alphabetized API reference) and communication with other users that highlights what we can do. That said, we could still improve reflection.

Yeah.. I think the user experience for using Julia code can be rough. I wish it was better for sure.

I have experimented with submodules and it has made the user experience much better, but I’m not sure if it is applicable in all situations.

Here was a post where I wrote about it.

Here it is in the docs of how I explain it: Getting Started · AISCSteel.jl

I also had some comments about OOP about a wish for a piping syntax that could mimic the obj.method LSP feature: Is Julia 2.0 needed? - #33 by co1emi11er

1 Like

Use my fork at Looking for opinion on dot methods completion implementation - #4 by xgdgsc . Some instructions at Release A quick dot methods completion demo · xgdgsc/julia-vscode · GitHub .