如何在不重启REPL的前提下运行修改后的代码

在开发时经常修改代码,为了不让过多的函数和类型定义污染全局命名空间,大部分函数和类型我都放在模块里了。问题是修改模块后重新运行不生效,每次都要重启REPL。例如有这样一个模块

module my_module
export Person, test1

struct Person
   name::String
end

function test1(a::Person)
   println("a's name $(a.name)")
end

end

和一个主函数

include("my_module.jl")
using .my_module

a = Person("David")
test1(a)

按照JuliaFAQ中的说法,将类型用模块裹起来就可以随时修改类型了。但是当我将代码修改如下后重新运行,类型定义并不更新,函数的修改也不生效。至少来说在全局作用域的函数只要重新运行一遍函数定义就可以生效了,但是在模块里的类型和函数修改后再include也没用。在不重启REPL的前提下有什么办法吗?(用的Julia v1.10.4)

module my_module
export Person, test1

struct Person
   name::String
   age::Float64
end

function test1(a::Person)
   println("a's age $(a.age)")
end

end

main.jl
include("my_module.jl")
using .my_module

a = Person("David",11)
test1(a)

WARNING: replacing module my_module.
WARNING: using my_module.Person in module Main conflicts with an existing identifier.
WARNING: using my_module.test1 in module Main conflicts with an existing identifier.
ERROR: MethodError: no method matching Person(::String, ::Int64)

Closest candidates are:
Person(::String)
@ Main.my_module \my_module.jl:5
Person(::Any)

Limitations · Revise.jl (timholy.github.io)

谢谢,尽管Limitations中只提到了类型、常量和同名函数和变量之间的冲突,但实际测试后我发现模块里的函数也没法被重编译(函数自身之间的冲突),我还是不用模块算了

1 个赞

你用Revise了?

用了,具体来说,当我将下述my_module.jl中的println(a * 2)改成println(a * 3)时,输出仍然是20.
my_module.jl

module my_module
export test1
function test1(a)
    println(a * 2)
end
end

main.jl

using Revise
include("my_module.jl")
using .my_module
test1(10)

修改前的输出:20
修改后的输出:WARNING: replacing module my_module.
WARNING: using my_module.test1 in module Main conflicts with an existing identifier.
20

你目录结构是标准Pkg.generate 生成的吗,按道理应该全局using Revise 然后在脚本using my_module 就行

我没有用Pkg.generate,我是用Vscode打开一个文件夹,在里面新建了几个.jl文件就开始写代码了……我不清楚这样和使用Pkg.generate在创建模块上有什么不同
稍后我会试一下使用Pkg.generate

User reference · Revise.jl (timholy.github.io) 这种情况要用includet

感谢您的帮助,用includet问题解决了。
总结一下本贴获得的经验:
一开始出现的问题在于Revise.jl根本没有跟踪到这些被修改的模块。可以用下面的命令来查看Revise.jl跟踪了哪些模块:

julia> Revise.watched_files

在没有使用PkgTemplates.jl时,可以用includet来跟踪指定的脚本和模块。
参考Julia 模块开发 - 保姆级教程,我用PkgTemplates.jl创建了一个模块“TestPackage”。

module TestPackage
export testa
function testa(a)
    print(a * 3)
end
end # module TestPackage

此时,目录结构看起来类似下面这样

TestPackage
├── Manifest.toml
├── Project.toml
├── src
│   └── TestPackage.jl
└── test
    └── runtests.jl

因为Project.toml里面有"TestPackage",所以可用以下命令让Revise跟踪到修改。

using Revise
using TestPackage
testa(10)

查看Revise.watched_files可以发现TestPackage已经在列表中。

但是以下的方法不能让Revise跟踪到修改

using Revise
push!(LOAD_PATH, pwd() * "\\src")
using .TestPackage
# or
include("..\\src\\TestPackage.jl")
using .TestPackage

也就是说,Revise跟踪不到使用include加载的模块和从加载路径加载的模块。

参考:

1 个赞

可以看下 Modern Julia Workflows

1 个赞