【问题标题】:Julia-lang: Extending a method from an installed module, "using" orderJulia-lang:从已安装的模块扩展方法,“使用”顺序
【发布时间】:2025-12-18 18:05:01
【问题描述】:

我正在尝试从 JSON.jl 模块扩展一个方法,特别是我正在这样做(扩展“JSON.lower”,如文档所述,以允许序列化), 一般来说 - 我问的是当前 Julia 从另一个模块扩展方法的“最佳实践”是什么 - 特别是 - 如果这是一种改变模块某些策略的方法(如JSON.jl )。

除了打补丁之外,真的有办法保证更新后的方法会被导入的模块使用吗?

让我们同时展示理想和邪恶的行为:

    module TestProgram
    using JSON
    import JSON.lower            # not sure whether this is needed
    JSON.print(Set([1,2,1]))
    ## output:
    ## {"dict":{"2":null,"1":null}}
    ## not the output I want or expect
    ## in this case might be a good idea to patch JSON.jl and pull request
    ## and "JSON.print" - is now compiled with this default behaviour

    JSON.lower{T}(v::Set{T}) = collect(v) # overloading 'JSON.lower'

    println(json(Set([1,2,1])))
    ## output: 
    ## [2,1]
    JSON.print(Set([1,2,1]))
    ## output still:
    ## {"dict":{"2":null,"1":null}}
    end # module
  • 如果之前在运行时的任何地方 - 一些代码已经运行了这个方法,json(v::Set{T}) - 那么这个方法已经编译,并且不会使用我的“JSON.lower”扩展
  • 这实际上发生在我身上并且很难调试,因为 json() 调用的行为符合预期,而 JSON.print() 的行为不同(只是通过更改某些运行流程)
  • 由于我正在使用的另一个包可以使用任何模块(此处为 JSON.jl) - 我无法知道我的代码实际上首先运行并实际正确扩展了一个方法

那么 - 确保这一点的最佳做法是什么?

[当前使用 Julia 0.4.6 和 0.5.0 测试了这种行为]

【问题讨论】:

  • 请注意,不建议为您不拥有的类型扩展 JSON.lower,因为 JSON 是许多其他人所依赖的重要包。如果您需要自定义 JSON 序列化,最好创建您自己的类型来包裹像 Set 这样的内置函数。
  • 酷,这就是我要做的,实际上 - 这 解决方案,因为没有其他代码会预编译带有包装的 Set 类型的版本,我可以在定义类型的地方定义。
  • 然而——在这种特殊情况下——我认为标准 JSON 包应该添加“Set”(一种基本类型)支持,序列化 Set 的低级输出,显示表示(dict,keys= >null ),如果 Set 实现发生变化,也会中断。我知道这可能会在某处破坏某些代码。
  • 你当然是对的;内部表示序列化不好。欢迎 PR 为 Set 添加合理的默认 lower

标签: module julia


【解决方案1】:

是的,您的分析在 Julia 0.5 及更早版本上是正确的。许多开发人员都知道这个问题编号 (#265)。为即将发布的 0.6 版修复此问题进行了大量工作; Julia 现在跟踪每个函数的调用者并根据需要重新编译它们。

不过,一般来说,这里最好的建议是直接修补库并推送您的更改。这在 0.5 和 0.6 中都是如此,即使有运行时正确性修复。建议您不要使用您没有定义自己的类型扩展导入的函数,因为它会改变 所有 其他依赖它的包的行为。这已成为通俗地称为“类型盗版”,因为您为自己的目的征用该方法 - 可能不适合其他调用者的目的。

作为一种临时解决方法,您可以尝试将定义添加到您的 ~/.juliarc.jl file,这将在启动时执行,并且更有可能在编译任何其他方法之前被定义。不过,即使这也不是万无一失的,因为该软件包可以使用预编译来加快其使用速度。

【讨论】: