You may be talking about interfaces. They are the right level of abstraction for some APIs, but there is nothing that requires that an API conforms to a particular interface. Nothing requires that all methods conform to a single interface. It may be bad API design, good API design, historical accident, etc.
In particular, collections evolved in Julia very gradually, and a lot of the inconsistencies you see are of historical origin. You will find that the collections and data structures part of the manual enumerates a couple of βloosely definedβ interfaces, in contrast to the interfaces section I linked above, and eg for findall & friends methods were added as the language evolved, eg findall(c::AbstractChar, s::AbstractString) came with Julia 1.7.
I agree that it is frustrating, but I think that small, piecemeal changes will be received better than large conceptual reorganizations. Eg it would be a worthy goal for a PR to clean up the docs and concepts for a small set of methods that are related.
For example, all methods of findall at the moment seem to be conceptually equivalent to
findall2(needle, haystack) =
[key for (key, value) in pairs(haystack) if neddle == value]
with the caveat that the output only matches the sorting when pairs is ordered. Of course the implementations are optimized, but you will find that the only thing that is conceptually needed for it to work is pairs.
But this is just my rational reconstruction, and while it happens to match the default method, it is not documented. So maybe it should be?
A lot of functions in Base could benefit from a little love along these lines. I think that PRs are ideal for that.
But then you are adding a default method to f. This choice might complicate debugging, because code that should have failed now unexpectedly returns 1.
And would this also cause type instability? If all other methods of f return a String, for instance, this might prevent the compiler to infer the correct return type.
Yes! That is the kind of βconceptsβ I have in mind.
Thatβs the very reason that I keep saying βconventionβ. Nothing βrequiresβ, but
A convention will βencourageβ its adoption.
In order to indicate the βintentionβ of the API, we can use the terms and ideas in the convention in the description of our APIs. Then, deviation from the βintentionβ will be discouraged.
When we write a new function, we will be encouraged to use the concepts in the convention.
So, yes, Iβd like to enhance the βinterfaceβ document, so that a newcomer will read it and understand it. If the official documentations use the ideas and terms in the interface document, it will become more readable to newcomers. Also, newcomers will starts to use the ideas in their own programs so that their programs will work with the standard library more harmoniously.
As I explained in one of my earlier posts, I have a first-hand experience in the last point. To summarize, I started to write a function with an array in mind, but I happened to end up with writing a function that works with any βindexablesβ. In the process, I asked questions in this forum, but not all people there were aware of these βinterfacesβ and recommended array-specific solutions.
This is a huge waste of resources. All julia programmers should know that the standard library is written around these βinterfacesβ and when you write your own function, you should ask yourself βDoes this argument has to be an array? Should I use contiguous integers starting from 1 to iterate over it?β
I think you misunderstood. @Imiq talked about the functionality of having an βextended help sectionβ. This has nothing to do with which method is actually documented. You can document each method of your liking with itβs own string which might contain βextended helpβ.
Code Example
"""
function f
generic help for f
# Extended help
extended help for f
"""
function f end
"""
function f(x)
specific help for f(Any)
# Extended help
extended help for f(Any)
"""
function f(x) return x end
"""
function f(x::Int)
specific help for f(Int)
# Extended help
extended help for f(Int)
"""
function f(x::Int) return 2x end
This will show up as:
help?> f
search: f fd for fma fld fld1 fill fdio frexp foldr foldl flush floor float
function f normal help - generic function
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function f(x) specific help for f(Any)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function f(x::Int) specific help for f(Int)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Extended help is available with `??`
help> ?f
search: f fd for fma fld fld1 fill fdio frexp foldr foldl flush floor float
function f normal help - generic function
Extended help
β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘
more info - generic function
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function f(x) specific help for f(Any)
Extended help
β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘
extended help for f(Any)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function f(x::Int) specific help for f(Int)
Extended help
β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘β‘
extended help for f(Int)
Also this has not really to do with type instability. If the compiler can infer the correct method to call and itβs output type, thatβs stable. There is no need to have the same return type across all possible functions. In fact f(x) = x is perfectly type-stable for all arguments