Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify extension docs about defining new symbols #3552

Merged
merged 3 commits into from Nov 15, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 35 additions & 9 deletions docs/src/creating-packages.md
Expand Up @@ -299,6 +299,8 @@ loaded into the Julia session. This is very similar to functionality that the ex
[Requires.jl](https://github.com/JuliaPackaging/Requires.jl) provides, but which is now available directly through Julia,
and provides added benefits such as being able to precompile the extension.

### Code structure

A useful application of extensions could be for a plotting package that should be able to plot
objects from a wide variety of different Julia packages.
Adding all those different Julia packages as dependencies of the plotting package
Expand Down Expand Up @@ -357,15 +359,6 @@ end # module
Extensions can have any arbitrary name (here `PlottingContourExt`), but using something similar to the format of
this example that makes the extended functionality and dependency of the extension clear is likely a good idea.

A user that depends only on `Plotting` will not pay the cost of the "extension" inside the `PlottingContourExt` module.
It is only when the `Contour` package actually gets loaded that the `PlottingContourExt` extension is loaded
and provides the new functionality.

If one considers `PlottingContourExt` as a completely separate package, it could be argued that defining `Plotting.plot(c::Contour.ContourCollection)` is
[type piracy](https://docs.julialang.org/en/v1/manual/style-guide/#Avoid-type-piracy) since `PlottingContourExt` _owns_ neither the method `Plotting.plot` nor the type `Contour.ContourCollection`.
However, for extensions, it is ok to assume that the extension owns the methods in its parent package.
In fact, this form of type piracy is one of the most standard use cases for extensions.

!!! compat
Often you will put the extension dependencies into the `test` target so they are loaded when running e.g. `Pkg.test()`. On earlier Julia versions
this requires you to also put the package in the `[extras]` section. This is unfortunate but the project verifier on older Julia versions will
Expand All @@ -376,6 +369,39 @@ In fact, this form of type piracy is one of the most standard use cases for exte
know about them, the extensions will not load. This is because the manifest lacks some information that tells Julia
when it should load these packages. So make sure you use a manifest generated at least the Julia version you are using.

### Behavior of extensions

A user that depends only on `Plotting` will not pay the cost of the "extension" inside the `PlottingContourExt` module.
It is only when the `Contour` package actually gets loaded that the `PlottingContourExt` extension is loaded too
and provides the new functionality.

In our example, the new functionality is an additional _method_, which we add to an existing _function_ from the parent package `Plotting`.
Implementing such methods is among the most standard use cases of package extensions.
Within the parent package, the function to extend can even be defined with zero methods, as follows:

```julia
function plot end
```

!!! note
If one considers `PlottingContourExt` as a completely separate package, it could be argued that defining `Plotting.plot(c::Contour.ContourCollection)` is
[type piracy](https://docs.julialang.org/en/v1/manual/style-guide/#Avoid-type-piracy) since `PlottingContourExt` _owns_ neither the function `Plotting.plot` nor the type `Contour.ContourCollection`.
However, for extensions, it is ok to assume that the extension owns the functions in its parent package.

In other situations, one may need to define new symbols in the extension (types, structs, functions, etc.) instead of reusing those from the parent package.
Such symbols are created in a separate module corresponding to the extension, namely `PlottingContourExt`, and thus not in `Plotting` itself.
If extension symbols are needed in the parent package, one must call [`Base.get_extension`](@ref) to retrieve them.
Here is an example showing how a custom type defined in `PlottingContourExt` can be accessed in `Plotting`:

```julia
ext = Base.get_extension(@__MODULE__, :PlottingContourExt)
if !isnothing(ext)
ContourPlotType = ext.ContourPlotType
end
```

On the other hand, accessing extension symbols from a third-party package (i.e. not the parent) is not a recommended practice at the moment.

### Backwards compatibility

This section discusses various methods for using extensions on Julia versions that support them,
Expand Down