I’m writing a Julia package that contains one module. The module includes many source files and functions. I want to develop new functions in the module without having to export every required function within the module so that they are visible to the new functions. In other words, I’d like the module functions to be in the namespace so I don’t have to qualify them or export each one as I develop. What’s the Julian way to do this?
I would get rid of the module when developing. Then, once I’m happy with the framework, protect everything with a module, and export public interface.
And this applies even when developing a Julia package? All the tutorials suggest creating a module called YourPackageName and then including files within the module. So you’re suggesting omitting the module from the package until some public interface is converged. Then wrap all includes with the module. Are there any side effects to this approach?
Actually, I personally never do this. It does make certain preliminary design decisions easier, but I always develop my packages as you outlined it. I don’t really see any disadvantage to it. But since you asked: one option is to expose everything initially.
Ok, I’ll try this mode of development and see if there are gotchas. Maybe some other Julia developers have something idiomatic? In general, I find the module/package/project system confusing right now, especially with respect to establishing a rapid development paradigm. Is there some reference to suggest best practices?
So do I get it right, that you want to have a module imported using using
but develop new functions for it in a separate script? That’s why you want all the functions from the module available?
I find that editing the files and functions in the module as the best way to develop.
So what I do:
Create a folder for the module and the basic files.
Create an environment in a separate folder, dev
the module into this environment.
Open in the editor files from the module - write functions etc. While in the other folder I have a file where I run the code. Everything is kept up to date, because I have Revise
package installed that reloads a module every time the code changes.
So I am not sure if this is what you mean by rapid development, but everything works with almost no lag.
The functions within the module all “see” each other. You mean testing them in the REPL?
I usually do MyModule.myfunction(...)
to use them while testing. A minor annoyance IMO.
In the upcoming Julia 1.9 release you can change the contextual module in the REPL:
$ cat src/TestPackage.jl
module TestPackage
function hello()
println("hello, world")
end
end # module
$ julia-master -q
julia> using REPL, TestPackage
julia> hello()
ERROR: UndefVarError: hello not defined
julia> REPL.activate(TestPackage)
(TestPackage) julia> hello()
hello, world
That’s nirvana. When is it expected to drop?
Until Julia 1.9 arrives, I will adopt this method. I find it awkward since I have to prefix my development code that relies on module dependencies that are not exported with the module name. This is the case even if the function I am developing is intended to be within the namespace of the module since I’m testing code snippets of the function in the REPL, which is not in the namespace of the module. I believe the context change will be a big benefit for this style of development, which I’m used to in Python and MATLAB.
feature freeze is tomorrow. we won’t know exactly when the release will be (since it depends on what bugs are found) but it should be in the 1-6 month timeframe.
I usually also do MM = MyModule; MM.myfunction(...)
to still reduce the annoyance.
Does Revise.jl invalidate the alias MM somehow?
Or
using MyModule
myfunction = MyModule.myfunction
?
No, it doesn’t
Well, sometimes I also do it this way. You can also (temporarily) export myfunction
. Just if MyModule_Has_A_LongName
and I wish to have an access to all it’s functions, I’m saving a couple of keystrokes by aliasing to a short form.
I use this pretty frequently
function exportall(mod)
for n in names(mod, all = true)
if Base.isidentifier(n) && n ∉ (Symbol(mod), :eval)
@eval mod export $n
end
end
end
Using your code snippet in combination with the suggestions of @mkoculak allows me to develop in the REPL (using Revise.jl) with nearly a seamless experience–something similar to MATLAB and python. I look forward to the context-aware v.1.9 to replicate this without brute force exporting all functions during development. Thanks for the responses. I’m happy with my dev environment now.
My trick is that I tend to put my entire module body in a file named “__assembly.jl” and then put the include statement in my module
module MyModule
include(joinpath(@__DIR__, "__assembly.jl"))
export <<all my exported functions here>>
end
When developing, I simply run “__assembly.jl”, otherwise, I run “MyModule.jl”