I seriously struggle to understand method extension in connection with using, import and export

Hi everybody!

I have a hard time to understand how method extension really works and I hope you can help me with that. Let’s look at a concrete example and talk about the DataStructures module with SortedSet.

So DataStructures.jl contains

import Base: show

and sorted_set.jl defines a method by

function Base.show(io::IO, m::SortedSet{K,Ord}) where {K,Ord <: Ordering}

which means that even though show is not exported in the DataStructure package, it can be called by show(mysortedset)from outside the module after loading the package with using DataStructures. What I don’t really understand here is why sorted_set.jl defines this method with Base.show instead of just show, which is what I would expect according to section 25.1 of the manual.
For example DataStructures.jl also contains

import Base: push!

ans sorted_set.jl defines a method by

@inline function push!(m::SortedSet, k_)

and this method can also be called outside the module by push!(mysortedset, somestuff) even tough it is not exported either. So where is the difference in this two cases? Or are they actually aquivalent? Or does this have to do with the @inline?

Even more trouble causes the method setdiff! for me. There is NO line like

import Base: setdiff!

(just import Base: setdiff, which I suppose to be a different name), but the method

function setdiff!(m1::SortedSet, iterable)

defined in sorted_set.jl can be called the same way as push!. That’s what I complete don’t understand since the method is not exported neither imported for method extension. From my understaind it should not be possible to call it with just settdiff!(mysortedset, somestuff), but I can confirm it is. My expectation would be, again referring to section 25.1 of the manual, that it must be called by DataStructures.settdiff!(mysortedset, somestuff).

I would really highly appreciate if someone was able to explain me what is going on here and tell me the point that I have missed so far. Thanks a lot!

In the first example, it looks like Base.show is redundant. That is, show would work as well. Alternatively, you could omit import Base: show, and then you would be required to use Base.show (to extend the function show from Base as intended.)

This is probably because DataStructures is too large and contains unrelated code that is developed by different people. There has been talk of splitting it.

I prefer to avoid importing from Base when possible, and then explicitly writing Base.show(.... This seems cleaner, signals intent at the method definition, and avoids redunancy causing the confusion that you are having (because you are not used to seeing this kind of thing.)

6 Likes

Thanks for the explanation. That makes a little more sense now:
So if you do import Base: show or import Base: push! you could extend these methods without the prefix Base., but you are not required to do so. If you omit the import statement, you must define it with Base.show(...) or Base.push!(...). That sounds logical to me now and I totally agree with you that the second version is way better to understand and read.

HOWEVER What about setdiff!, which contraticts this.
I mean it is defined with setdiff!(...) on the one hand, but there is no Base: setdiff! on the other hand. That should not actually work, but it does. Could it be that I am wrong when I assume that setdiff and setdiff! are different and they are actually somehow considered the same? I mean there is Base: setdiff in the package definition, so that would make sense!

It is imported explicitly.

Most packages would put imports in the main source file, but that is merely a style convention.

When in doubt, you can always check these things interactively, eg

julia> import DataStructures

julia> parentmodule(DataStructures.setdiff!)
Base
2 Likes

:smiley:
It’s obiously a pretty good idea to stick to that convention. Looking for the command in another files of the package, which are not even related to SortedSet is something I would have never done.

But the command parentmodule is new to me! Thanks for that one! It should help a lot!

It is generally a good idea to be able to use a variant of grep when learning about the internals of a package. I use ag, integrated into Emacs; possibly your IDE has something similar, or you can use the command line.

1 Like

I will try to find that out! Thanks a lot for your useful advice!