Suppose I have MyPkg which has as an extension MyExt. I basically always want MyPkg to use MyExt if that package is available in the environment. Is there a way to automate it so that import MyPkg always does import MyPkg, MyExt if MyExt is available?
Better yet, I don’t even want MyExt in the upper level namespace, so someway that this would happen internal to MyPkg would be even better. Basically it would activate the extension not if MyExt is loaded but if MyExt is available in the environment and MyPkg is loaded.
The best solution I’ve found so far would be to make a separate module, something like:
module MyPkg_Ext
using Reexport
@reexport using MyPkg
import MyExt
end
Then I could do import MyPkgExt as MyPkg, but that’s kind of clunky.
This part is impossible, extensions aren’t intended to be exposed namespaces, that’s what packages are for.
I don’t understand why this is an extension to begin with, I might be misreading this. If MyExt is attached to your package MyPkg like extensions are, but you want MyExt to be loaded solely based on MyPkg being imported, why isn’t MyExt just part of MyPkg? It can be a submodule if you need the separate namespace.
Here I meant MyExt to be the package that is loaded that causes the internal extension module to get loaded. Let me use rewrite the example based on Code Loading · The Julia Language
So in my case I’d have a Project.toml for MyPackage that looks like:
That’s clearer, thanks, I was thrown off because the trailing Ext is the convention for the extension itself, not the extension dependencies.
This isn’t done because it’s not sound design. If ExtDep isn’t a mandatory dependency of, or in other words imported by, MyPackage, then you are asserting you can import ExtDep on its own or MyPackage on its own in a Julia process, and it follows that loading FooExt depends on you explicitly importing both.
Instead of unexpected deviations from that rule, the existing way to do what you want is to make MyPackage import ExtDep. FooExt only depends on MyPackage’s import and thus doesn’t even need to be an extension, it can just be part of MyPackage after ExtDep’s import. Extensions are a cool and relatively new feature but it doesn’t redundantly supplant established features.
Okay thank you. The idea here is that MyPackage is public but ExtDep is a private package. MyPackage works fine without ExtDep, but if ExtDep is available it unlocks a bunch of features in MyPackage. Thus, if a person has it available in an environment (i.e., they have permission to use ExtDep), it would be great for it to get loaded and used by MyPackage as smoothly as possible. It’s analogous to MyPackage being the free version of a software and ExtDep being a plugin that you pay for. If a user has paid for ExtDep, you’d like MyPackage to see and to use that seamlessly.
You’re trying to make the environment itself designate what code is loaded by a package’s import, and that is not at all what extensions are designed for nor is it sound design. Emphasizing that packages aren’t just low-level code caches but also high-level namespaces in an interactive runtime, where would ExtDep go? Is it loaded to the same parent module I’m importing MyPackage into, as if I ran using MyPackage, ExtDep, or inside the MyPackage module itself as if it were a true dependency? It may seem obvious to you, but people can reasonably expect the implicit behavior to be either, or even something else entirely. Either way, how would you resolve conflicts of the name ExtDep and possibly its exported names with separate names belonging to the parent module or MyPackage? You could deliberately avoid those names in MyPackage yourself as the developer, but users can’t be expected to do the same in their parent modules. As you already intuited, you can best control names by making a 3rd reexporting package for users to add instead, but even those few lines seem like an unsatisfactory amount of work to trim an import statement, doesn’t it? Hence your search for something automatic.
Not that those namespace issues have a satisfying automatic solution, but even if it could be designed, it shouldn’t. On a practical level, what if I want to add MyPackage and ExtDep to my environment yet import them completely independently, possibly in different sessions or in different modules within the same session? This would not only take that choice from me, it would scale poorly to more independent packages forcing each other’s imports in an opaque indefinite web across the ecosystem (package A forces B, B forces C, etc), vastly increasing load times for code I didn’t even need in a given session. This is only preventable when 1 isolated import has 1 behavior no matter the state of the environment; extensions don’t have this propagating issue because they’re only triggered by 1 fixed set of packages, not other extensions. This isn’t the first time someone wanted extensions to behave like automatically loaded packages, but that’s not what they are, and despite a couple similarities, they lack many aspects of packages to keep programming sane; extensions are really just extensions for packages, not alternatives.