Creating and using a custom module in Julia - module

Although this question has been asked before, it appears that much has changed with respect to modules in Julia V1.0.
I'm trying to write a custom module and do some testing on it. From the Julia documentation on Pkg, using the dev command, there is a way to create a git tree and start working.
However, this seems like overkill at this point. I would like to just do a small local file, say mymodule.jl that would be like:
module MyModule
export f, mystruct
function f()
end
struct mystruct
x::Integer
end
end # MyModule
It appears that it used to be possible to load it with
include("module.jl")
using MyModule
entering the include("module.jl"), it appears that the code loads, i.e. there is no error, however, using MyModule gives the error:
ArgumentError: Package MyModule not found in current path:
- Run `import Pkg; Pkg.add("MyModule")` to install the MyModule package.
I notice that upon using include("module.jl"), there is access to the exported function and struct using the full path, MyModule.f() but I would like the shorter version, just f().
My question then is: to develop a module, do I need to use the Pkg dev command or is there a lighter weight way to do this?

In order to use a local module, you must prefix the module name with a ..
using .MyModule
When using MyModule is run (without the .), Julia attempts to find a module called MyModule installed to the current Pkg environment, hence the error.

Related

How to remove a package from defpackage?

I defined a package like this:
(defpackage :web-app
(:nicknames :wa)
(:use :cl :hunchentoot))
This works fine.
But I want to remove hunchentoot. When I remove it and recompile I get the following error:
Unknown location:
warning:
WEB-APP also uses the following packages:
(HUNCHENTOOT)
See also:
Common Lisp Hyperspec, DEFPACKAGE [:macro]
SBCL Manual, *ON-PACKAGE-VARIANCE* [:variable]
How do I remove packages from my lisp image in these cases.
I have tried restarting the image but no luck.
In this case the function to use is unuse-package. For example:
(unuse-package :hunchentoot :web-app)
That will sync the package system with your defpackage form so it will re-evaluate without a warning.

Loading Module in Julia

i'm having some trouble loading module in Julia. I have to module that i cant load in my main file.
So my code (i'm trying to make an octree) look like this:
module Node
export node, contains, intersect
mutable struct node
x::Float64
y::Float64
z::Float64
m::Float64
node(x,y,z) = new(x,y,z,0)
end
end # module
and my other module:
module Tree
include("Node.jl")
using .Node
export tree, insert!, subdivide!
mutable struct tree
capacity::Int64
node::node
divided::Bool
tree(capacity, node) = new(capacity, node, false)
end
end
My problem is when i try to import the module in my main file using something like that:
include("Node.jl")
using .Node
include("Tree.jl")
using .Tree
plop=node(0,0,0)
plip=tree(1,plop)
I get the following error:
ERROR: LoadError: MethodError: Cannot `convert` an object of type node to an object of type Main.Tree.Node.node
I understand its due to the using .Node in my tree module which conflict with the same import in the main file but i'm unable to find a workaround.
One solution would probably be to put everything in the same module but i would like to keep thing separated.
Well, you actually just put the two modules in the same module. Or to be more precise, you have a module Node and a module Tree with a submodule Node in it, thus the Main.Tree.Node.node. This happens because you use include("Node.jl") within your Tree module. The include function works as if it copied the text in the Node.jl file and pasted it into the Tree.jl file. Thus, to use the Node module within Tree without creating a submodule you have to add it.
So, I'd recommend you to generate a package for both the Node and Tree modules. This is done by
julia> using Pkg
julia> Pkg.generate("Node")
Generating project Node:
Node/Project.toml
Node/src/Node.jl
and then copy your Node.jl and Tree.jl files to replace the ones that were created.
Then you can look at this question that tells you how to add a local package.
To sum it up you need to
julia> Pkg.develop(path="/Path/to/Node")
julia> Pkg.develop(path="/Path/to/Tree")
then your /Path/to/Tree/src/Tree.jl looks like
module Tree
using Node
[...]
end
and to run your code you can type
julia> using Node, Tree
julia> plop=node(0,0,0)
node(0.0, 0.0, 0.0, 0.0)
julia> plip=tree(1,plop)
tree(1, node(0.0, 0.0, 0.0, 0.0), false)
Note that it will probably tell you that Tree does not have Node in its dependencies. To solve that you might want to have a look at the documentation of Pkg.jl.

How do I reload a module in an active Julia session after an edit?

2018 Update: Be sure to check all the responses, as the answer to this question has changed multiple times over the years. At the time of this update, the Revise.jl answer is probably the best solution.
I have a file "/SomeAbsolutePath/ctbTestModule.jl", the contents of which are:
module ctbTestModule
export f1
f1(x) = x + 1
end
I fire up Julia in a terminal, which runs "~/.juliarc.jl". The startup code includes the line:
push!(LOAD_PATH, "/SomeAbsolutePath/")
Hence I can immediately type into the Julia console:
using ctbTestModule
to load my module. As expected f1(1) returns 2. Now I suddenly decide I want to edit f1. I open up "/SomeAbsolutePath/ctbTestModule.jl" in an editor, and change the contents to:
module ctbTestModule
export f1
f1(x) = x + 2
end
I now try to reload the module in my active Julia session. I try
using ctbTestModule
but f1(1) still returns 2. Next I try:
reload("ctbTestModule")
as suggested here, but f1(1) still returns 2. Finally, I try:
include("/SomeAbsolutePath/ctbTestModule.jl")
as suggested here, which is not ideal since I have to type out the full absolute path since the current directory might not be "/SomeAbsolutePath". I get the warning message Warning: replacing module ctbTestModule which sounds promising, but f1(1) still returns 2.
If I close the current Julia session, start a new one, and type in using ctbTestModule, I now get the desired behaviour, i.e. f1(1) returns 3. But obviously I want to do this without re-starting Julia.
So, what am I doing wrong?
Other details: Julia v0.2 on Ubuntu 14.04.
The basis of this problem is the confluence of reloading a module, but not being able to redefine a thing in the module Main (see the documentation here) -- that is at least until the new function workspace() was made available on July 13 2014. Recent versions of the 0.3 pre-release should have it.
Before workspace()
Consider the following simplistic module
module TstMod
export f
function f()
return 1
end
end
Then use it....
julia> using TstMod
julia> f()
1
If the function f() is changed to return 2 and the module is reloaded, f is in fact updated. But not redefined in module Main.
julia> reload("TstMod")
Warning: replacing module TstMod
julia> TstMod.f()
2
julia> f()
1
The following warnings make the problem clear
julia> using TstMod
Warning: using TstMod.f in module Main conflicts with an existing identifier.
julia> using TstMod.f
Warning: ignoring conflicting import of TstMod.f into Main
Using workspace()
However, the new function workspace() clears Main preparing it for reloading TstMod
julia> workspace()
julia> reload("TstMod")
julia> using TstMod
julia> f()
2
Also, the previous Main is stored as LastMain
julia> whos()
Base Module
Core Module
LastMain Module
Main Module
TstMod Module
ans Nothing
julia> LastMain.f()
1
Use the package Revise, e.g.
Pkg.add("Revise") # do this only once
include("src/my_module.jl")
using Revise
import my_module
You may need to start this in a new REPL session. Notice the use of import instead of using, because using does not redefine the function in the Main module (as explained by #Maciek Leks and #waTeim).
Other solutions: Two advantages of Revise.jl compared to workspace() are that (1) it is much faster, and (2) it is future-proof, as workspace() was deprecated in 0.7, as discussed in this GitHub issue:
julia> VERSION
v"0.7.0-DEV.3089"
julia> workspace()
ERROR: UndefVarError: workspace not defined
and a GitHub contributor recommended Revise.jl:
Should we add some mesage like "workspace is deprecated, check out Revise.jl instead"?
Even in Julia 0.6.3, the three previous solutions of workspace(), import, and reload fail when a module called other modules, such as DataFrames. With all three methods, I got the same error when I called that module the second time in the same REPL:
ERROR: LoadError: MethodError: all(::DataFrames.##58#59, ::Array{Any,1}) is ambiguous. Candidates: ...
I also got many warning messages such as:
WARNING: Method definition macroexpand(Module, ANY) in module Compat at /Users/mmorin/.julia/v0.6/Compat/src/Compat.jl:87 overwritten in module Compat at /Users/mmorin/.julia/v0.6/Compat/src/Compat.jl:87.
Restarting the Julia session worked, but it was cumbersome. I found this issue in the Reexport package, with a similar error message:
MethodError: all(::Reexport.##2#6, ::Array{Any,1}) is ambiguous.
and followed the suggestion of one contributor:
Does this happen without using workspace()? That function is notorious for interacting poorly with packages, which is partly why it was deprecated in 0.7.
In my humble opinion, the better way is to use import from the very beginning instead of using for the reported issue.
Consider the module:
module ModuleX1
export produce_text
produce_text() = begin
println("v1.0")
end
println("v1.0 loaded")
end
Then in REPL:
julia> import ModuleX1
v1.0 loaded
julia> ModuleX1.produce_text()
v1.0
Update the code of the module and save it:
module ModuleX1
export produce_text
produce_text() = begin
println("v2.0")
end
println("v2.0 loaded")
end
Next, in the REPL:
julia> reload("ModuleX1")
Warning: replacing module ModuleX1
v2.0 loaded
julia> ModuleX1.produce_text()
v2.0
Advantages of using import over using:
avoiding ambiguity in function calls (What to call: ModuleX1.produce_text() or produce_text() after reloading?)
do not have to call workspace() in order to get rid of ambiguity
Disadvantages of using import over using:
a fully qualified name in every call for every exported name is needed
Edited: Discarded "full access to the module, even to the not-exported names" from "Disadvantages..." according to the conversation below.
workspace() has been deprecated.
You can reload("MyModule") in an active REPL session, and it works as expected: changes made to the source file that contains MyModule are reflected in the active REPL session.
This applies to modules that have been brought into scope by either import MyModule or using MyModule
I wanted to create a new module from scratch, and tried the different answers with 1.0 and didn’t get a satisfactory result, but I found the following worked for me:
From the Julia REPL in the directory I want to use for my project I run
pkg> generate MyModule
This creates a subdirectory like the following structure:
MyModule
├── Project.toml
└── src
└── MyModule.jl
I put my module code in MyModule.jl. I change to the directory MyModule (or open it in my IDE) and add a file Scratch.jl with the following code:
using Pkg
Pkg.activate(".")
using Revise
import MyModule # or using MyModule
Then I can add my code to test below and everything updates without reloading the REPL.
I battled to get Revise.jl to work for me, probably because I also use code generation with OOPMacro #class . I had to also call revise(...) See https://timholy.github.io/Revise.jl/stable/limitations/#Limitations-1.
I have been struggling with this problem for up to 5 years and finally got something that works for me that don't involve manually running my main module in the IDE repl after EVERY SMALL CHANGE :'(
Here is some of my code I use to force a revise and exit early when running tests when there were revise errors:
# Common code I include into my test files now:
using Pkg
using Revise
# force include module
Pkg.activate("MyModule")
include("../src/MyModule.jl")
Pkg.activate("MyModule/test")
using Revise
# if there were any revise errors which means some compilation error or
# some change that requires a manual rerun or restart.
# then force you to fix it, rather that running lying tests..
for (k, e) in Revise.queue_errors
if !isnothing(e)
warn(logger, "Something went wrong while revising, you probably have a compile error in this file:")
throw(e)
end
end
module TestSomethingModule
using Revise
using MyModule
revise(MyModule)
...
# then you can call MyModule.doSomithing in a test and it actually updates
end

From a module, consult a file in another module

For some reason, in my Prolog module I want to be able to consult a file, but like if it was consulted from another module (e.g. user).
Say I have a file named myfile.pl containing:
foo(1).
Normally, if in module mymodule I execute consult(myfile), what I have is a new predicate mymodule:foo/1.
I would like to be able to consult the file from module mymodule but the resulting predicate be like user:foo/1.
Is this possible?
Did you tried to call consult(user:myfile) from within your mymodule module?

How to use a dependency of a module within a Play app

I am writing a Play Framework module in order to share some common logic among multiple Play apps. One of the things I would like my module to do is provide some frequently-used functionality by way of 3rd-party modules, for example the excellent Markdown module.
First of all, is it possible to do this? I want all the apps that include my module to be able to use the .markdown().raw() String extension without needing to explicitly declare the Markdown module as a dependency. The Play Framework Cookbook chapter 5 seems to imply that it is possible, unless I am reading it wrong.
Secondly, if it is possible, how does it work? I have created the following vanilla example case, but I'm still getting errors.
I created a new, empty application "myapp", and a new, empty module "mymod", both in the same parent directory. I then modified mymod/conf/dependencies.yml to:
self: mymod -> mymod 0.1
require:
- play
- play -> markdown [1.5,)
I ran play deps on mymod and it successfully downloaded and installed the Markdown module. Running play build-module also worked fine with no errors.
Then, I modified myapp/conf/dependencies.yml to:
# Application dependencies
require:
- play
- mymod -> mymod 0.1
repositories:
- Local Modules:
type: local
artifact: ${application.path}/../[module]
contains:
- mymod
I ran play deps on myapp and it successfully found mymod, and generated the myapp/modules/mymod file, containing the absolute path to mymod.
I ran myapp using play run and was able to see the welcome page on http://localhost:9000/. So far so good.
Next, I modified myapp/app/views/Application/index.html to:
#{extends 'main.html' /}
#{set title:'Home' /}
${"This is _MarkDown_, by [John Gruber](http://daringfireball.net/projects/markdown/).".markdown().raw()}
I restarted myapp, and now I get the following error.
09:03:23,425 ERROR ~
#6a6eppo46
Internal Server Error (500) for request GET /
Template execution error (In /app/views/Application/index.html around line 4)
Execution error occured in template /app/views/Application/index.html. Exception raised was MissingMethodException : No signature of method: java.lang.String.markdown() is applicable for argument types: () values: [].
play.exceptions.TemplateExecutionException: No signature of method: java.lang.String.markdown() is applicable for argument types: () values: []
at play.templates.BaseTemplate.throwException(BaseTemplate.java:86)
at play.templates.GroovyTemplate.internalRender(GroovyTemplate.java:257)
at play.templates.Template.render(Template.java:26)
at play.templates.GroovyTemplate.render(GroovyTemplate.java:187)
at play.mvc.results.RenderTemplate.<init>(RenderTemplate.java:24)
at play.mvc.Controller.renderTemplate(Controller.java:660)
at play.mvc.Controller.renderTemplate(Controller.java:640)
at play.mvc.Controller.render(Controller.java:695)
at controllers.Application.index(Application.java:13)
at play.mvc.ActionInvoker.invokeWithContinuation(ActionInvoker.java:548)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:502)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:478)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:473)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:161)
at Invocation.HTTP Request(Play!)
Caused by: groovy.lang.MissingMethodException: No signature of method: java.lang.String.markdown() is applicable for argument types: () values: []
at /app/views/Application/index.html.(line:4)
at play.templates.GroovyTemplate.internalRender(GroovyTemplate.java:232)
... 13 more
And just to confirm I'm not crazy, I tried adding the play -> markdown [1.5,) line to myapp/conf/dependencies.yml and restarted the app, and confirmed that it works.
I feel like I'm missing something obvious. Many thanks in advance to anyone who can help! :)
Yes I had the same problem, it seems that transitive dependencies through custom home made modules does not work