That’s quite impressive. Perhaps they can update their original question with the answer, as I have yet not found it.
Let’s see if we can do with Julia what we can do with Python…
Python:
Let’s define a class:
class Foo:
x = 1
def bar(self):
return self.x
Tab-complete shows the property x
as well as the member method bar
.
Julia:
The Julian way of writing this is to recognize that Foo
’s member method bar
probably generalizes across a range of other Foo
-like types, so we define an AbstractFoo
type and specialize our methods on it. (We could specialize to Foo
, and that would do exactly the same thing as having a class member method, but that’s not as Julian.) First we write Foo
-specialized methods which directly access its fields, and then write methods of AbstractFoo
to call them.
abstract type AbstractFoo end
Base.@kwdef struct Foo <: AbstractFoo
x = 1
end
getx(foo::Foo) = foo.x
bar(foo::AbstractFoo) = getx(foo)
Because we have syntax sugar for getproperty
(namely .
dot), and because I have typed Foo()
, it knows to call propertynames
on this object (or something similar). Because I have typed the object description, and typed a dot, autocomplete has the information it needs to help me discover x
.
However, it’s not Julian to access the object’s properties directly; the preferred idiom is instead to call methods on it. So let’s find those methods.
Unfortunately, the situation isn’t so good for helping me discover either bar(::Foo)
, or getx(::Foo)
:
What I claim is simple: that one day, autocomplete will recognize that I intend to call a function that specializes on a Foo
, and it will help me find it. However, because the pipe operator can only call single-argument functions, at the moment such an autocomplete would not be very useful; we need a preferred partial application technique first, so that we can capture the whole range of possible method signatures.
Unless, of course, we should just solve the problem by making every method a dotted member method:
abstract type AbstractFoo end
Base.@kwdef struct Foo <: AbstractFoo
x = 1
bar = function(self) self.x end
end
Base.getproperty(x::AbstractFoo, n::Symbol) = begin
if getfield(x,n) isa Function
return (a...;k...)->getfield(x,n)(x,a...;k...)
end
getfield(x,n)
end
Now we finally get method discoverability in Julia, but it shouldn’t be this hard (or entirely non-Julian) to do.
That’s not the problem I care about, because I do not care about solving impossible problems. I would have no time left for living life if I did.
You can consider a “member method” to be simply a function which is specialized to exactly the concrete type of the class. When hoping to tab-complete on member methods, I’m trying to find the most specialized functions! Why should I wish to hit tab to see all the methods which are not specialized to this object, nor to any object like it? I can just start typing random function names anyway.
Recall from inception the purpose of Julia, which is essentially to be a scripting language which compiles: to have the benefits of quick dev time and then quick run time. Scripting languages are quick and easy to develop in, partly because of not needing to assign types to objects, sure ok, but also because if you want to scratchpad something you can just casually start scratchpadding. You can hop into a REPL and experiment, and when you’ve found the appropriate methods, algorithms, and fragments of glue code you can copypaste back into the editor; or you can hit CTRL+ENTER in VSCode, or etc. It’s part of the preferred development style.
For sections of code where arguments are fully generic to ::Any
, and if you’re also not scratchpadding the code and getting global vars from it, I don’t see why anyone would expect autocomplete to help.