It’s actually not that rare. Modules are the only way to create namespaces in Julia, and namespaces can have quite fine-grained use cases. An example is EnumX.jl, which provides an improved @enum
using a module under the hood. It wouldn’t be very ergonomic if each enum had to live in a separate file.
Do you know about the ability to enter into a module at the REPL? The Julia REPL · The Julia Language
This has definitely been discussed a lot in the past. You can find almost all of them just by searching for the number 4600. It’s also something that’s fundamentally very near-and-dear to folks hearts: how you prefer to organize your code is very opinionated and widely varies and can be quite different between languages.
I did not know about that, that is interesting, thanks for the tip.
Maybe I’m just misinterpreting this, but how is include
-ing a file to evaluate an unpackaged module and importing its names into other modules substantially different from Rust’s option to insert a file’s text into a mod
definition and use
to import names into other modules? Rust even made that decision despite 1-file-per-module being idiomatic, see Why would rust use both mod and use? - help - The Rust Programming Language Forum
Well, yes. Packages are (and must be) modules, but not all modules are packages. An installed, versioned, and compiled unit can’t be handled the same way as your own source code. Rust has packages, crates, and mod
-ules too. If anything it seems like Python is the odd one out here.
Sorry, I likely misunderstood your intent, specifically your post’s phrasing “dynamically load and replace code” and “dynamically replace types, functions, modules and packages.” That said, I’m evidently not alone in failing to see what the intent is; all this stuff about imports don’t really seem relevant to your initial Python example for reassigning exampleModule1
to a different function in the REPL.
I largely agree with that assessment.
A positive effect of this discussion has been that it prompted me to read up on the details of the module/package system again. This made me realise that I had misunderstood a few things and that the system is actually very consistent.
However, in my opinion there are two issues:
- Having
use
/import
handle modules and packages without any obvious visible difference on the users’ side leads to endless amounts of confusion, especially for people coming from languages with a file ↔ module correspondence. - There is no obvious, lightweight solution for the (in my mind very common) use case “encapsulate a local chunk of code and load it like a package”.
There is no obvious, lightweight solution for the (in my mind very common) use case “encapsulate a local chunk of code and load it like a package”
The lightest-weight solution is probably to modify LOAD_PATH
manually. Although this has been discouraged previously in the thread, there’s currently no alternative if you want to load modules like packages without dealing with the overhead of creating a new package.
Although it’s not framed this way, the manual section on package directories is really an explanation of how Julia deals with entries in LOAD_PATH
that don’t have the full structure of a package. It’s not written in a user-friendly way, so here’s the CliffsNotes version:
- Put your code in a file
- Wrap it in a top-level module such that the module name exactly matches the file name
- Put the file in the package directory[1]
Now the module can be loaded like a package. Example:
- My package directory is
./code
- It contains a single file
code/Foo.jl
with the following contents:module Foo export foo foo() = println("Hello from Foo.jl") end
- Now let’s load
Foo
like a package:julia> push!(LOAD_PATH, "code"); # add ./code to LOAD_PATH julia> using Foo [ Info: Precompiling Foo [top-level] julia> foo() Hello from Foo.jl
I think this is the closest you can currently get to a python-like import .file
experience. The only extra overhead is making sure the module and file names match, and a single push to LOAD_PATH
.
Alternatively, put the file in a package-like folder structure inside the package directory, that is, instead of adding
Foo.jl
you can addFoo/src/Foo.jl
↩︎
Yes, I’ve been doing that for my own projects for a while. The problem is that this stops working as soon as a project.toml
exists, since then all package dependencies have to be declared explicitly.
Can you give an example of when this occurs? (None of the previous examples in the thread use package directories.) I assume you’re referring to something caused by the following rules (from the manual):
- A package with a project file cannot depend on one without a project file since packages with project files can only load packages in
graph
and packages without project files do not appear ingraph
.- A package with a project file but no explicit UUID can only be depended on by packages without project files since dummy UUIDs assigned to these packages are strictly internal.
I assume that’s the reason, yes. In practice I realised it when I started preparing a package from a project I had only used locally until then.