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.

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)

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