I have a question: I can extend the standard library by inserting methods into other namespaces. E.g.:
struct Fun
end
function Base.close(fun::Fun)
print("Fun can't be closed")
end
Is it generally a good idea or a bad idea to extend functions this way?
I come from C++, where putting anything in the STL’s namespace is considered a Very Bad Thing. So I expect “no,” but I’m in a discussion elsewhere with somebody who thinks it’s the right thing to do. Is there a stylistic convention or technical reason to do things one way or the other?
No, it’s not really a problem, in fact it is sometimes necessary (e.g. to change how an object prints you need to define a Base.show method).
The one thing you should avoid is what has become called “type piracy”, basically defining new methods for types which your package didn’t itself define. So your example is okay (since you defined Fun), but it would not be okay to define say Base.close(s::String).
When you are worried about type piracy, you can also use ForceImport to make it an optional extension,
Which I previously explained on here before
This way you don’t have to extend the Base methods by default and modified methods can be located inside your custom module; while giving the option to locally force their import contained in a namespace with @force using Module, giving you the best of both worlds locally and globally.
This is totally fine in Julia, and we do it all the time.
When defining a new method for a function, you just need to make sure that either:
At least one of the argument types is from your module
Or
The function itself is from your module.
Your example above satisfies option 1, so you’re good.
Type piracy happens when you don’t satisfy either condition, like if you were to define Base.close(x::Array). That’s bad because it can cause other code that doesn’t use your functions or types at all to mysteriously change behavior.
In fact, it’s an important part of the design of Julia. You are expected to add methods to a function (e.g from Base) for semantically similar operations. Functions are not owned by data types.
Here are the modules defining methods for print in Base and the stdlib, and packages that happen to be loaded. There are many more methods for print in other packages
julia> using StatsBase;
julia> countmap([m.module for m in methods(print).ms])
Dict{Module,Int64} with 14 entries:
LibGit2.Consts => 17
Base.Docs => 2
Base => 14
LibGit2 => 1
Base.Grisu => 2
Pkg.Types => 5
Pkg.REPLMode => 3
LibGit2.Error => 2
REPL.TerminalMenus => 1
DataFileHeaders => 1
Sockets => 2
Base.MPFR => 2
Base.Multimedia => 1
Distributed => 1
These aren’t separate object methods, but rather methods of a single function.