Git Product home page Git Product logo

usingmerge.jl's Introduction

UsingMerge.jl

This package 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. 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> 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> using BenchmarkTools: @btime

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

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> 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:

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

while BenchmarkTools has four:

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:

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):

@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> 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> @usingmerge BenchmarkTools: invariants, ratio

The macro @usingmerge has two optional arguments

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> @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.

usingmerge.jl's People

Contributors

jmichel7 avatar

Stargazers

 avatar Martin Kirk Bonde avatar Rafael Guerra avatar Diego Javier Zea avatar Andrey Oskin avatar

Watchers

Dilum Aluthge avatar  avatar

usingmerge.jl's Issues

StackOverflowError during import of DataFrames

It looks like the package uses too broad assumptions. It seems that if package exports already extended function everything go sideways.

When trying to do @usingmerge DataFrames I encounter the following error

using UsingMerge
@usingmerge verbose=3 DataFrames

# skipping lines

# DataFrames.convert conflicts with Base.convert --- adding methods#= /home/skoffer/.julia/
packages/UsingMerge/jdqiP/src/UsingMerge.jl:86 =# @doc #= /home/skoffer/.julia/packages/Usi
ngMerge/jdqiP/src/UsingMerge.jl:86 =# @doc(DataFrames.convert) Base.convert
 and docs
(Base.convert(::Type{T}, x::Ptr) where T <: Integer) = DataFrames.convert(T, x)
(Base.convert(::Type{Pair{A, B}}, x::Pair{A, B}) where {A, B}) = DataFrames.convert(Pair{A,
 B}, x)
(Base.convert(::Type{Pair{A, B}}, x::Pair) where {A, B}) = DataFrames.convert(Pair{A, B}, x
)
Base.convert(::Type{IOContext}, io::IO) = DataFrames.convert(IOContext, io)
(Base.convert(::Type{Some{T}}, x::Some{T}) where T) = DataFrames.convert(Some{T}, x)
(Base.convert(::Type{Some{T}}, x::Some) where T) = DataFrames.convert(Some{T}, x)
(Base.convert(::Type{LinearAlgebra.Adjoint{T, S}}, A::LinearAlgebra.Adjoint) where {T, S})
= DataFrames.convert(LinearAlgebra.Adjoint{T, S}, A)
ERROR:
SYSTEM (REPL): showing an error caused an error
ERROR:
SYSTEM (REPL): caught exception of type StackOverflowError while trying to handle a nested
exception; giving up

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.