JuMP currently exports a pretty arbitrary set of symbols:
https://github.com/JuliaOpt/JuMP.jl/blob/040841ef2ca2f0a4054bde4da4eff1be90fe0948/src/JuMP.jl#L26-L36
We should try to come up with a coherent convention before the 0.19 release.
Options:
We could define internal functions as anything whose name begins with _. (So far we haven't consistently been following this convention.)
4 Is very vague. 3 is still vague and not future-proof because it's hard to keep track of what names exist in Base/stdlib. 2 is principled but makes it hard to import JuMP and anything else via using. 1 is principled but less user friendly. No clear winner.
I vote for export nothing except the macros.
I agree with the macro rule we might get a usability loss if we don't export Model and with_optimizer.
Maybe it is a sign that we need a @model macro :-掳
What's special about macros that only they should be exported? Does this imply that packages that don't have macros shouldn't export anything?
The first line of the README is "JuMP is a domain-specific modeling language". For this reason, I think the modeling part should be exported, which means:
Model and with_optimizerPSDCone, SecondOrderCone and RotatedSecondOrderConeThe rest shouldn't be exported, as it is used when programming in Julia, not in the domain-specific language of JuMP.
Is optimize! part of the DSL?
My concern about the DSL criteria is that it doesn't provide any guidance for what to export from packages that aren't DSLs.
Can't we make 2 and 3 the same? why is it needed to have names that conflict with Base/stdlib ?
Assuming we can't make 2 and 3 the same, and say someone has a conflict with optimize! then they can always have a using on the things they need which do not make conflict for them. Ex:
using JuMP: Model, with_optimizer, @variable, @constraint, @objective
I think this would be better than JuMP. inside the code which is too verbose for a modeling language.
My concern about the DSL criteria is that it doesn't provide any guidance for what to export from packages that aren't DSLs.
Why is that ? In SumOfSquares, this rule implies that SOSCone and SOSPoly should be exported because they are used in @constraint and @variable just like PSDCone is exported while SumOfSquares.certificate_monomials should not be exported just like JuMP.value isn't.
Why is that ? In SumOfSquares, this rule implies that SOSCone and SOSPoly should be exported because they are used in
@constraintand@variablejust like PSDCone is exported while SumOfSquares.certificate_monomials should not be exported just like JuMP.value isn't.
I was hoping for a more general principle than "export things needed for JuMP as a DSL". In particular, that doesn't say anything about what to export from packages that aren't related to JuMP and aren't DSLs. I guess we don't need to solve that issue though.
@IssamT Yes, the option to explicitly list the imported names is always available. That doesn't tell us what we should export though. Are you saying that we should export everything (e.g., optimize!, value) and let users explicitly import if they need to, or that we should export nothing and force users to explicitly import?
I like solution 2 (exporting everything except internal functions) because it offers more freedom to the user. At least for teaching, JuMP was in our courses a replacement to OPL (a modeling and a scripting language), in which case having all macros/functions available with using JuMP, except internal functions, is desirable. In advanced packages one may want to use a cleaner coding style and only import JuMP.
I agree that its incredibly better for beginners if they don't need to JuMP. anything commonly used. So I vote with @IssamT for solution 2, and a user can use import JuMP if desired.
In some courses there is even some students with little or zero experience on programming.
This could also be solved by having a BeginnerJuMP (training wheels style) that simply does the exports... but since using and import do the work we could go for it.
If we go with 2 then we need:
_ is external and exported.Here's an implication of option 2. If the rule is "export everything that doesn't start with an underscore", then solver packages will export Optimizer, so you get in trouble quickly with using JuMP, Gurobi, Ipopt. (Both Gurobi and Ipopt would export Optimizer). Of course, we can backtrack from Optimizer to GurobiOptimizer and IpoptOptimizer, but that's a loss for those using import.
For the purposes of the 0.19 milestone, let's export what we think is reasonable for tutorials. I think we're running into bigger questions about how to use exports that nobody has a good answer to yet.
I am inclined to agreed with @joaquimg's proposal but I would reverse the naming. JuMP.jl would include two packages along these lines,
JuMPAPI - exports nothing, this is the only package that should be used by JuMP extensionsJuMP - only exports things from JuMPAPI (no added functionality) and possibly MOI, the purpose of JuMP is to make it easy to teach and write quick and dirty scripts.This does not really answer the question of what should be exported by JuMP, but I would be inclined to say all non-internal functions; using descriptive names can be used to avoid clashes with other packages.
@ccoffrin I don't see a reason to separate JuMP into JuMP and JuMPAPI. Serious code should use import JuMP which has the same effect as using JuMPAPI in your proposal.
using descriptive names can be used to avoid clashes with other packages
What does this imply? That we shouldn't use Model or value or optimize!? I don't see how it's practical to choose names with the goal of avoiding clashes with the global namespace that's implied by all of the exports of common Julia packages.
If import is the best practice for serious code, then I don't see any advantage of the export nothing proposal. I would thus vote for export everything non-internal; if a user experiences clashes they can switch from using to import.
Serious code should use import JuMP which has the same effect as using JuMPAPI in your proposal.
Are you suggesting that library code (or anything more than a script Jupyter notebook) should be using only import and never using?
I have not seen such a recommendation before, so maybe it should be present in the style guide, then?
@leethargo #1778
Agreed that there's no reason to export nothing if we adopt the recommendation in #1778 to not use using in serious code. The analogy to from <module> import * in python is pretty convincing for me.
Ok, so how can we export all non-internal functions? If we follow the python analogy, everything except symbols starting with _ is exported. Is this implementable in Julia? (I'll dig in.)
Option 2 is implemented in #1779.
If serious code should use import then JuMP extensions should use import.
However we were discussing with @chriscoey that we might recommend JuMP extension to reexport JuMP symbols:
https://github.com/JuliaOpt/SumOfSquares.jl/pull/68
So that the user could do using SumOfSquares without the need to do using JuMP (because it is needed 99% of the time).
For this reason, we do
@reexport using JuMP
and it seems that
@reexport import JuMP
is not supported. What is the recommendation for that ?
EDIT I asked the question on discourse to see if that's technically possible.
Most helpful comment
The first line of the README is "JuMP is a domain-specific modeling language". For this reason, I think the modeling part should be exported, which means:
Modelandwith_optimizerPSDCone,SecondOrderConeandRotatedSecondOrderConeThe rest shouldn't be exported, as it is used when programming in Julia, not in the domain-specific language of JuMP.