One limitation of the functional approach is discoverability: which functions can be applied to a given type? This is not necessary however: We (should) know all the functions which take an object of given type as first argument. This leads to the possibility of code completion:
abstract type Animal end
struct Cat <: Animal end
struct Fish <: Animal end
swims(Cat)=false
swims(Fish)=true
pussy=Cat()
pussy.swims() # <--- NEW syntax, same as swims(pussy), but discoverable via <tab>
The important point is that it doesn't change the fundamentals of the language at all**, it's just syntactic sugar with the huge benefit of discoverability in the REPL. It might also hugely increase adoption.
Corollary: An inverse 'methods' function which lists all the methods for a given type (can be huge).
(** I've read https://discourse.julialang.org/t/psa-julia-is-not-at-that-stage-of-development-anymore/44872 )
Your swims
methods are busted (should be swims(::Cat) = false
). After fixing that, methodswith(Cat)
works.
But...why should we privilege the first argument? Julia itself doesn't, and it's a crucial advantage that it does not.
If your tab key is feeling neglected, I'd much rather see something like
?(x, yTAB
start listing options for ?
depending on the types of x
and y
.
I completely understand this argument, and it is often discussed as an advantage of "dot-style" object-oriented languages. But adding this to julia is really not realistic. Imagine I filed an issue with python saying "I don't like the dots, can you please allow function-call syntax for all methods?"
I don't think this is "just" syntactic sugar. It's arguably misleading for e.g. x.length()
to look for length
in the current scope instead of in x
or its type. It's also not even clear how it should work, since x.y
is already overloadable syntax. When do we call getproperty
vs. looking for a y
function in the calling scope? I mean that rhetorically --- even if some solution is possible I think that basic conflict would make the language significantly more confusing. Somewhat ironically, our python interop (PyCall.jl) relies on x.y()
working the way it does currently --- rewriting it to y(x)
would prevent PyCall from working.
One syntax that I've toyed with in my head is named infix operator syntax: i.e. the generalization of x in y
. The tricky bit comes if you want to apply it to multiple arguments and if you want to chain it. There's also the issue that Julia functions don't generally take arguments in an obj.f(args...)
friendly order. For example, you want to write collection.map(transform)
but the order of arguments is the traditional functional order: map(transform, collections...)
. You'll note that this order is more flexible in general since you can map a transform over many collections. What would you do in object syntax? Write (a, b, c).map(transform)
?
If the key point is tab-completion, doesn't ?(x, yTAB
basically solve the problem? Just a question of someone PRing it.
Most helpful comment
Your
swims
methods are busted (should beswims(::Cat) = false
). After fixing that,methodswith(Cat)
works.But...why should we privilege the first argument? Julia itself doesn't, and it's a crucial advantage that it does not.
If your tab key is feeling neglected, I'd much rather see something like
start listing options for
?
depending on the types ofx
andy
.