Overwrite function in a submodule

Hi everyone,

I have a Base module that contains some functionality, which I would like to (optionally) extend upon.
Ideally, the extension simply overrides some functions in the base module, while leaving the others as they are.
One option which comes to mind is to define a submodule, which imports the desired function, however the following code does not work as intended:

module BaseModule
    export g
    f() ="Base Module"
    function g()
        print("Version is: ")
        println(f())
    end 
    module Extension
        export g
        using ..BaseModule
        import ..BaseModule:f
        f() = "Extension"
    end
end 

Calling the function g gives:

julia> BaseModule.Extension.g()
Version is: Extension

julia> BaseModule.g() #I'd like this to print "Version is: Base Module" instead
Version is: Extension

Can anyone help me in this? Is there maybe a better way achieving the desired result?
Other ideas, I’ve had:

  • Create two separate modules, with the second one importing the first. → I get nasty warnings if i want to simply overwrite the function from another module

  • Create both modules in the same repository, each including the respective definitions independently. → Pkg.add only adds the first module so I would always have to include the file with the definition of the second module.

Looking forward to hear some interesting suggestions from you.

I don’t see any warning from this (not importing f in the Extension module)

module BaseModule
    export g
    f() ="Base Module"
    function g()
        print("Version is: ")
        println(f())
    end 
    module Extension
        export g
        using ..BaseModule
        f() = "Extension"
    end
end 

And it works as expected for me (v1.5 at least)

julia> BaseModule.Extension.f()
"Extension"

julia> BaseModule.f()
"Base Module"

you’re overriding this function. Please don’t do this, Julia is not Class-OOP, please don’t use Module as hacky “Class” for “dispatching”

1 Like

I understand why this looks hacky to you, it does to me too. Any idea of how to accomplish this idea without getting cursed by the Julia gods?

a function from a different modules (normally speaking, ) should never “replace” each other completely. If they are unrelated, use name space, A.f() and B.f(), if they are related, use multi dispatch

1 Like

Yes, multiple dispatch is probably the solution here. In my case, the extension module does not require me to define a new type, so there seems no natural way to use multiple dispatch here. Of course, I could define a dummy type which is different for both modules and pass it to the functions to distinguish them, but that also feels slightly unelegant.

Edit: fixed missing word

does or does not?

sorry, that was a typo, I meant to say it does not need another type

if it doesn’t need a new type, and you’re doing (spiritually) the same thing f() to the same data, but it’s somehow different, why should this function be called f(), it should be called B.f() (again, just use name space to distinguish) or advance_f()

In essence, f is a setup function that is later used by another function g and g is further used. Since f is the only function that changes, I would prefer to keep the name so that I do not need to modify all the other functions down the line. I suppose another way would be to modify g to take f as an input, though

yeah. If g() rely on a setup function but that setup function is “hot swapable”, you should make that an input and have a default maybe. Think count. which by default the by function is identity, this pattern is common.

silently mutating the f() that g() ends up calling is pretty hacky, and hard to undo from the user end if they somehow uses your extension package but want to do the “original” thing – it ends up being a import-order-dependent issue then?

1 Like

Thank you for your help. I accept your argument that this is probably the best option, so I marked it as a solution. (Of course I still have interest in hearing if there are other approaches to this).
To answer your question: Yes, I suppose it does. Although my intention was not really that both modules really have to be used within the same scope (Its a module for my own purposes that is unlikely to be of use to others), it is still better to do this without breaking it if one decides to do so anyway.

1 Like