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

julep: conditional modules #6195

Closed
vtjnash opened this issue Mar 18, 2014 · 31 comments
Closed

julep: conditional modules #6195

vtjnash opened this issue Mar 18, 2014 · 31 comments
Assignees
Labels
design Design of APIs or of the language itself kind:julep Julia Enhancement Proposal kind:speculative Whether the change will be implemented is speculative modules

Comments

@vtjnash
Copy link
Sponsor Member

vtjnash commented Mar 18, 2014

currently, some modules (incorrectly and unfortunately) use Pkg.installed to decide whether it should include some additional functionality. I would like to propose the following extension to module initialization that could help simplify this situation of optional dependencies:


Rules:

  1. During module parse time, a module can declare a dependency on another module, given by name
  2. Following the closing of a module (without error), and after all __init__ functions have run, any modules that declared module B require Main.C will be (re-)loaded.

Example:

module A
  module B requires Main.C
    # code in here is part of A iff Main.C is defined as a module
    # it's execution time is defined as after the call to A.__init__ and C.__init__
    using Main.C
    import ..A.something
    something(::C.CType) = 2
  end
  # code in here is always part of A
  something(::Any) = 1
end
# Accessing A.B here would throw an UndefRef-like error, with a message that B requires C
module C
  type CType end
end
# Now A.B exists, and A.something() has extra functionality
@vtjnash vtjnash added the julep label Mar 18, 2014
@JeffBezanson
Copy link
Sponsor Member

Using modules to delimit optional code seems like a potentially very good idea.
It's not good for it to inherently require reloading code though.

@timholy
Copy link
Sponsor Member

timholy commented Mar 18, 2014

I was mentally going over similar ground yesterday. Would it be reasonable to declare that interacting code can't be loaded until after the types are defined?

@ivarne
Copy link
Sponsor Member

ivarne commented Mar 18, 2014

Very similar to #2025

I do not get the crucial point here. Why does anything need to be reloaded? I would assume that most of this could be solved with a defer_require(mod::String, file::String) method that defers loading of the file until a module with name mod is loaded into Main.

@vtjnash
Copy link
Sponsor Member Author

vtjnash commented Mar 18, 2014

The comment about reloading would only be triggered in the case where the user replaced the module. However, perhaps that condition does not need to be defined.

Using modules to define the separation allows Julia to pre-compile and cache the results (in the future). Thus, it is almost just as flexible, but it can be optimized to be fast.

@StefanKarpinski
Copy link
Sponsor Member

I wonder if parametric modules are relevant here. Consider that Main.C could be viewed as a parameter to A.B – you're declaring, in effect, a module template A.B{C} where the actual C to specialize that template on is to be provided later by some other code.

@vtjnash vtjnash self-assigned this May 9, 2014
@vtjnash
Copy link
Sponsor Member Author

vtjnash commented May 17, 2014

@StefanKarpinski that sounds rather clever. however, who would be responsible for instantiating the necessarily templates?

@StefanKarpinski
Copy link
Sponsor Member

Julia?

@vtjnash
Copy link
Sponsor Member Author

vtjnash commented May 17, 2014

I mean, how would Julia know which templates to instantiate if the user wrote:

module X{A}
end

what is A allowed to be?

@StefanKarpinski
Copy link
Sponsor Member

That would be up to the user. If they wrote using X{Foo} then A would be Foo. This would inherently allow multiple instances of modules.

@quinnj
Copy link
Member

quinnj commented Jun 22, 2014

What's the reasoning of using modules for optional code vs. arbitrary blocks of code? Something like:

when A
    using A
    # do stuff with module A
    # define functions, call methods, optionally wrap them in a module, etc.
elsewhen B
    using B
    # if A isn't loaded, but B is; do stuff with B
else
    # fallback if A or B can't be found; could just throw a "Need A or B error"
end

There's probably some good implementation detail to require modules, but I can imagine there are a lot of cases where you really only need a few lines, so not needing the submodule would be convenient.

@vtjnash
Copy link
Sponsor Member Author

vtjnash commented Jun 24, 2014

  1. modules are the only interface we have for reloading code (relatively) cleanly
  2. this functionality has nothing to do with what modules can be found (state), and operates instead against the event of loading a module. an else clause is meaningless in such context.
  3. creating a new module is the only interface we have for adding entirely new exports & functions after 'closing' a module

this is all covered in the discussion on the pull-request thread

@jiahao
Copy link
Member

jiahao commented Feb 12, 2015

I've run into a use case which could really benefit from conditional package loading in JuliaLinearAlgebra/IterativeSolvers.jl#35

@rleegates
Copy link

Any progress on this topic? I really feel the need for conditional imports :)

@StefanKarpinski
Copy link
Sponsor Member

I've been wanting this to be merged for about six months now.

@mbauman
Copy link
Sponsor Member

mbauman commented Feb 24, 2015

Requires.jl is a very nice stop-gap that has worked well for my purposes.

@rleegates
Copy link

What's standing in its way? I'm having trouble seeing how your pull request helps my issue. Basically I have a module Big that does a bunch of stuff even when a dependency Dep is missing, however if it's installed, additional functionality is provided. As far as I understood your pull request, the whole module Big requires the dependency, if it's missing, the module Big isn't loaded. Is this interpretation correct?

@StefanKarpinski
Copy link
Sponsor Member

@vtjnash, have you abandoned this issue? You haven't commented on it since almost a year ago.

@vtjnash
Copy link
Sponsor Member Author

vtjnash commented Mar 22, 2015

not entirely, i was just finally getting back to even older issues (#2818) and have no shortage of other unresolved proposals (https://github.com/JuliaLang/julia/pulls/vtjnash)

@sbromberger
Copy link
Contributor

sbromberger commented Sep 20, 2016

Yeah. We had to break out a bunch of things into a separate module in order to keep LightGraphs from imploding due to fragility issues with all the dependencies. We also now have a policy in place with respect to PRs that severely restricts what we allow in LightGraphs now (not a good thing):

  • PRs introducing dependencies on core Julia packages are ok.
  • PRs introducing dependencies on non-core "leaf" packages (no subdependencies except for core Julia packages) are less ok.
  • PRs introducing dependencies on non-core non-leaf packages require strict scrutiny and will likely not be accepted without some compelling reason (urgent bugfix or much-needed functionality).

Conditional dependencies would go a long way to reducing the problems of "dependence fragility" within Julia packages. (We don't want to get to a point where Node found itself a few months back.)

@tkelman
Copy link
Contributor

tkelman commented Sep 20, 2016

#15705 is the latest thinking. needs an implementation that ties into precompilation correctly. in the meantime, when it comes to conditional dependencies, either disable precompilation or don't do things conditionally. Make separate packages for features that have additional dependencies that won't always be needed by a "main" package.

@StefanKarpinski
Copy link
Sponsor Member

The latest latest thinking (based on a discussion @JeffBezanson, @vtjnash and I had last week), is that what we want is very similar to what I originally proposed in #2025 – i.e. have a mechanism whereby packages, when they're loaded, can register a module for later loading once some set of modules have all been loaded. Note that if you want code for A that depends on both B and C then you'll want to wait until all of A, B and C have been loaded, so this isn't just a binary relation.

@tbreloff
Copy link

This is the "Requires" model, and if that was built-in and played well with
precompilation it would be a big win!

The other use case is when you want to do something only when a package is
installed (not just when it's explicitly loaded).

I think both uses would share the tricky parts of the implementation,
namely how to defer code execution until some event, and how to make it
play well with precompilation. I just hope the "on install" event is also
able to be supported, in addition to the "on import" event.

On Tuesday, October 11, 2016, Stefan Karpinski [email protected]
wrote:

The latest latest thinking (based on a discussion @JeffBezanson
https://github.com/JeffBezanson, @vtjnash https://github.com/vtjnash
and I had last week), is that what we want is very similar to what I
originally proposed in #2025
#2025 – i.e. have a mechanism
whereby packages, when they're loaded, can register a module for later
loading once some set of modules have all been loaded. Note that if you
want code for A that depends on both B and C then you'll want to wait until
all of A, B and C have been loaded, so this isn't just a binary relation.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#6195 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AA492sUprUHIe7TxkDpchkQBPG_zmm6xks5qzB0SgaJpZM4Bqetc
.

@sbromberger
Copy link
Contributor

sbromberger commented Oct 11, 2016

The problem we had with Requires.jl (and the if defined :packagename workaround) was that any use of macros from an external package meant that it couldn't be conditionally loaded. This affected us primarily because of JuMP.

@tbreloff
Copy link

any use of macros from an external package meant that it couldn't be conditionally loaded

But wasn't that because Requires was an external hack to try to get the functionality we wanted? If this was built into the core of the language, presumably macros could be deferred until they were ready to be run.

@JeffBezanson
Copy link
Sponsor Member

A bit more on @StefanKarpinski 's description above: #15705 (comment)

@richardreeve
Copy link

I see this has been moved to a 1.0 milestone, so I presume this is a way off now, but is it still in someone's plans, because it would be incredibly useful!

@StefanKarpinski
Copy link
Sponsor Member

Yes, we really need this in 1.0 – it's a big problem for the package ecosystem currently.

@richardreeve
Copy link

Good to know, thanks.

@richardreeve
Copy link

@StefanKarpinski So does this mean conditional modules are not going into 1.0 any more? Or are they already implemented somewhere?

@hayd
Copy link
Member

hayd commented Oct 19, 2017

@richardreeve see #2025 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Design of APIs or of the language itself kind:julep Julia Enhancement Proposal kind:speculative Whether the change will be implemented is speculative modules
Projects
None yet
Development

No branches or pull requests