Add types/functions to existing module (that I don't own)

Hey there, I’ve been using Julia for a while but am new to the forum.

I’ve been thinking about how to write more flexible code, and I haven’t been able to figure this one out (or prove it’s impossible), so I thought I’d ask the experts.

Let’s say I have a module that’s already been written by someone else and has code like this:

module NumPackage

export compute, do_thing

function compute(x)
    println("do some stuff")
    do_thing(x)
end

do_thing(x::Int64) = x+1

end # module

Obviously I wrote this illustrative example code, but I’d prefer to not have to touch it, so I could handle similar code written by third parties.

Say I want to add a new type in a new module like this:

module NumType

export DoubleTrouble, do_thing

struct DoubleTrouble
    x::Int64
    y::Int64
end

function do_thing(dt::DoubleTrouble)
    return DoubleTrouble(dt.x+1,dt.y+1)
end

end # module

When I write code to use these together, I get an error:

julia> using NumPackage
julia> using NumType
julia> compute(5)
do some stuff
6
julia> compute(DoubleTrouble(5,9))
do some stuff
ERROR: MethodError: no method matching do_thing(::DoubleTrouble)
Closest candidates are:
  do_thing(::Int64) at ~/dev/julia/NumPackage/src/NumPackage.jl:10
Stacktrace:
 [1] compute(x::DoubleTrouble)
   @ NumPackage ~/dev/julia/NumPackage/src/NumPackage.jl:7
 [2] top-level scope
   @ none:1

So, I have 2 questions:

  1. Is there anything I could do in NumType and/or Main to get compute(x) to find and use do_thing(dt::DoubleTrouble)?
  2. Especially if there’s not a good solution to 1, is the code in NumPackage less than ideal? If so, how could it be improved to make extension easier?

Also, I did find an earlier topic that was somewhat similar, but it required modifications to their equivalent of NumPackage and all the modules involved were submodules.

1 Like

Your package should be something like:

module NumType

import NumPackage.do_thing

export DoubleTrouble, do_thing

struct DoubleTrouble
    x::Int64
    y::Int64
end

function do_thing(dt::DoubleTrouble)
    return DoubleTrouble(dt.x+1,dt.y+1)
end

end # module

See that I added a line after module…
So in your package you need to import the definition of do_thing and then extend it, so that Julia can know that they are the same. You also need to have NumPackage in NumType’s Project.toml, etc.

3 Likes

That worked.

For future readers, here’s a solution if you can’t touch the code of either NumPackage or NumType:

module ComboPackage

export do_thing, DoubleTrouble

using NumPackage
using NumType

import NumPackage: do_thing

do_thing(x::DoubleTrouble) = NumType.do_thing(x)

end # module

This also works:

module ComboPackage

export do_thing, DoubleTrouble

using NumPackage
using NumType

NumPackage.do_thing(x::DoubleTrouble) = NumType.do_thing(x)

end # module

Also, I’m calling ComboPackage like:

julia> using NumPackage
julia> using ComboPackage
julia> compute(DoubleTrouble(4,7))
do some stuff
DoubleTrouble(5, 8)
julia> NumPackage.do_thing
do_thing (generic function with 2 methods)
1 Like

Note that extending methods that you don’t own on types that you also don’t own is what we know as type piracy. I can be just fine some times but it may be catastrophic for other cases.

https://docs.julialang.org/en/v1/manual/style-guide/#Avoid-type-piracy

3 Likes