How to extend different internal methods based on the module I load?

For example, if I have a function outer that has an internal implementation inner, and I want to extend inner in different modules.

module Outer

export outer

function outer(a, b)
    # do something
    return inner(a, b)

function inner end

module A

import ..Outer

Outer.inner(a, b) = a + b


module B

import ..Outer

Outer.inner(a, b) = a * b



As you can see, outer is my public API. And I want it to behave differently if I load different modules. Usually, I can define 2 methods on the types of args a and b. But in this example, a and b have fixed types, so no dispatch on types. The expected behavior is that if I load A, outer(1, 2) -> 3, and once I write using Outer.B in the same REPL environment, outer(1, 2) -> 2.

julia> using .Outer

julia> outer(1, 2)
# 1 method for generic function "inner":
[1] inner(a, b) in Main.Outer.B at REPL[1]:252

If I invoke outer, inner defined in A is rewritten by that defined in B. I tried Base.invokelatest and it gave the same result.

Don’t do this. It does not mesh well with how Julia works, and it is bad style.

Instead, you could use a parameter to outer that is passed to inner and participates in dispatch.

Traditionally this would be done if module B implemented a custom object that it wanted processed by module A . Like:

module B
   import ..Outer

   struct BbObj
    Outer.inner(a::BbObj, b) = # whatever needs to happen.

Yes, but I cannot do that. For example, let’s say outer takes a::Int and b::Int. So there’s no way to distinguish the two inners using type-based dispatch. What I am trying to do is like dispatch inner on module name A and B.

If two methods are named the same and take the same parameters, then there is no way to distinguish between the two, and the one that is most recently defined will “win” and be the method that is called.

If you need to do that then you need to pass in the function in the method call so it can be called back.