# UsingMerge: merge method definitions when using a package

**URL:** <https://discourse.julialang.org/t/usingmerge-merge-method-definitions-when-using-a-package/61885>\
**Category:** Package Announcements\
**Tags:** namespaces\
**Created:** [May 26, 2021, 8:40pm UTC](https://discourse.julialang.org/t/usingmerge-merge-method-definitions-when-using-a-package/61885 "2021-05-26T20:40:37Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jean\_Michel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jean_michel/32/8282_2.png) [@Jean\_Michel](https://discourse.julialang.org/u/Jean_Michel)\
**Post date:** [May 26, 2021, 8:40pm UTC](https://discourse.julialang.org/t/usingmerge-merge-method-definitions-when-using-a-package/61885/1 "2021-05-26T20:40:37Z")

</div>

This package (now registered) exports a single macro `@usingmerge` which differs from  
`using` in that it “merges” the exported definitions automatically.

The wish for this started in  
[this thread](https://discourse.julialang.org/t/function-name-conflict-adl-function-merging/10335/7).  
At the time I was very new to Julia and did not think I could do anything  
myself about the problem. Two years later, knowing better Julia, I realized  
I could do something. Here it is.

I introduce the problem with an example.

In my `Gapjm` package (a port of some GAP libraries to Julia) I have a  
function `invariants` which computes the invariants of a finite reflection  
group. However when I use `BenchmarkTools` to debug for performance my  
package, I have the following problem:

```julia
julia> G= # some group...

julia> invariants(G)
WARNING: both Gapjm and BenchmarkTools export "invariants"; uses of it in module Main must be qualified
ERROR: UndefVarError: invariants not defined
Stacktrace:
 [1] top-level scope at REPL[4]:1
 [2] eval(::Module, ::Any) at ./boot.jl:331
 [3] eval_user_input(::Any, ::REPL.REPLBackend) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
 [4] run_backend(::REPL.REPLBackend) at /home/jmichel/.julia/packages/Revise/kqqw8/src/Revise.jl:1163
 [5] top-level scope at none:0

```

This is annoying! I do not want to have to qualify every call to my  
function `invariants` just because I am timing my code! What can I do?  
Well, first I could just import the methods I am using in `BenchmarkTools`:

```julia
julia> using BenchmarkTools: @btime

```

Actually, every exported name from `BenchmarkTools`, except `invariants`,  
does not conflict with my code:

```julia
julia> names(BenchmarkTools)
30-element Array{Symbol,1}:
 Symbol("@ballocated")
 Symbol("@belapsed")
 Symbol("@benchmark")
 Symbol("@benchmarkable")
 Symbol("@btime")
 Symbol("@tagged")
 :BenchmarkGroup
 :BenchmarkTools
 :addgroup!
 :allocs
 :gctime
 :improvements
 :invariants
 :isimprovement
 :isinvariant
 :isregression
 :judge
 :leaves
 :loadparams!
 :mean
 :median
 :memory
 :params
 :ratio
 :regressions
 :rmskew
 :rmskew!
 :trim
 :tune!
 :warmup

```

so I can do:

```julia
julia> using BenchmarkTools: @ballocated, @belapsed, @benchmark, @benchmarkable, @btime, @tagged, BenchmarkGroup, BenchmarkTools, addgroup!, allocs, gctime, improvements, isimprovement, isinvariant, isregression, judge, leaves, loadparams!, mean, median, memory, params, ratio, regressions, rmskew, rmskew!, trim, tune!,
 warmup

```

Still no conflict. Can I go further and do something even for `invariants`?  
Well, I have one method for `invariants` in my package:

```julia
invariants(a::Group, args...)

```

while `BenchmarkTools` has four:

```julia
invariants(group::BenchmarkGroup)
invariants(x)
invariants(f, group::BenchmarkGroup)
invariants(f, x)

```

Even though some of these last methods apply to `Any`, they do not conflict  
with my method (since at least one of the arguments of my method, the  
first, is qualified with my type `Group`), so I can use them also by just  
defining:

```julia
invariants(group::BenchmarkGroup) = BenchmarkTools.invariants(group)
invariants(x) = BenchmarkTools.invariants(x)
invariants(f, group::BenchmarkGroup) = BenchmarkTools.invariants(f, group)
invariants(f, x) = BenchmarkTools.invariants(f, x)

```

The last thing to do is make the docstring of `BenchmarkTools.invariants`  
accessible to the help of `invariants`. It happens it has no docstring, but  
if it had one I must do (this _adds_ to the doc of `invariants`):

```julia
@doc (@doc BenchmarkTools.invariants) invariants

```

I call the end result of the above process `merging` the package  
`BenchmarkTools` with my current package. What I announce here is a  
macro `@usingmerge` which does all the above automatically. If you do

```julia
julia> using UsingMerge
julia> @usingmerge BenchmarkTools

```

The function determines conflicting method names in the package and merges  
them as above, and does `using` of the non-conflicting names.

Just like for `using` you can `usingmerge` only some of the names of the  
package

```julia
julia> @usingmerge BenchmarkTools: invariants, ratio

```

The macro `@usingmerge` has two optional arguments

```julia
julia> @usingmerge reexport BenchmarkTools

```

will reexport all non-conflicting names (a conflicting name is by  
definition already present in your environment and will be exported if you  
did export it).

```julia
julia> @usingmerge verbose=true BenchmarkTools

```

will print all conflicts resolved, and `verbose=2` will print all executed  
commands.

You will find some more information in the docstring of `@usingmerge`.

Since I wrote this function, I found that I got the hoped for modularity  
benefits in my code. For example, I have in my `Gapjm.jl` package modules

- `Perms` Permutations
- `Cycs` Cyclotomic numbers (sums of complex roots of unity)
- `Pols` Univariate Laurent polynomials
- `Mvps.jl` Multivariate Puiseux polynomials
- `Posets.jl` Posets
- `FFields.jl` Finite fields

that I designed as independent, stand-alone packages which each can be used  
without importing anything else from my package. To use them together I can  
now just `using_merge` each of them instead of writing (unpleasant) glue  
code.

I do not advocate always replacing the semantics of `using` with that of  
`@usingmerge`, but I feel that `@usingmerge` is a nice tool for using  
packages together without having to write glue code (and without having to  
modify any of the used packages). The meaning of “pirating a type” becomes  
a little bit wider in this context, as you saw with the method  
`invariants(y)` in `BenchmarkTools`: it is, I would say, polite, if any of  
your methods which has a possibly conflicting name uses at least one of  
your own types in its signature.

The program only merges methods of functions. If a conflicting name is a  
`macro`, a `struct` or a type, a message is printed and the name is not  
merged.

Any kind of feedback will be welcome. My implementation is perhaps not the  
best, as I kind of parse the printed output of `methods`. Accessing the  
internal structure of the returned object would be better but I do not know  
what’s officially accessible in there. If you have any comments on the  
code, functionality or documentation please contact me.
