Two modules with the same exported function name but different signature

Hello, I have the following situation which I did not expected: defining a fucntion with the same name but different signature in two modules like this:

## Module RawTables
function save(T::RawTable, name_pref::String)
...
end

## Module RawPlots
function save(T::RawPlot, name_pref::String)
...
end

and using them together results in

WARNING: both RawPlots and RawTables export "save"; uses of it in module Main must be qualified

I thought multiple dispatch is exactly good in being able to resolve this issue for me? Basically it is only function overloading in this case and there simply cannot be any problems about what method to call.

What is happening here and how can I resolve this? Thanks

Just because two generic function (which is the container of the methods) have the same name, does not mean that they are the same object (you can check this with ===). You need to extend the generic function defined in one module in the other; just like with Base functions:

julia> module A
       export f
       f() = 1
       end                                                                                                                                                             
Main.A                                                                                                                                                                 

julia> module B
       using ..A
       A.f(x) = 2
       end                                                                                                                                                             
Main.B      
                                                                                                                                                           
julia> using .A                                                                                                                                                        
                                                                                                                                                                       
julia> f()                                                                                                                                                             
1                                                                                                                                                                      
                                                                                                                                                                       
julia> f(1)                                                                                                                                                            
2                                                                                                                                                                      
2 Likes

Thanks. But this implies that the modules have to know about each other, which is not useful for anything else. And I have to specify the hierarchy which module extends the other one?

Maybe there can be some check whether the name exists and extend it or otherwise create a new object?

I mean, e. g. in this example save is a very common name but yet it is not in Base. Does this mean, that everyone needs to know about evry other module which possibly also uses save and extend it?

This has been discussed at length, the latest was Function name conflict: ADL / function merging?.

1 Like

No; just that you have to know about another module if you want to extend methods of that specific module when developing a module. Otherwise you just have to specify which modules version you intended to use when actually using both modules at the same time.

Thank you for the link. Well, I have looked through it and I clearly would vote for a possibility to have common function names from different modules to work together smoothly without the need to import them or state the module explicitly :wink:

But this is only my option…

They already do; if you only import one of them, that warning is not going to show up. It shows up in the example provided by @mauro3 because they’re both contained in the top level scope Main and are referencing each other and are thus loaded by default in the REPL.

You only have to do this when developing a module and explicitly want to add to that modules functions method tables. You are not required to qualify every module that might potentially have a save function.

1 Like

This is exactly the problem I have. Neither do I extend the save behavior nor do I want to specify the module name because I call the save function with a type which clearly can decide the right version of the function for me.

In my option this is a very clear case of “do the same thing” bit still belong to different modules.

I can understand that this can lead to errors on the big scale but for some very common words which are not in Base I really would like to see a way to do this. For convenience, not because it is logically the best option for a programming language and all this discussion about “doing the same thing”.

You should hook into https://github.com/JuliaIO/FileIO.jl for load/save.

1 Like

You made my day. A logical and obvious solution which I did not considered. Thanks

1 Like

Cool!

That is one of the pros of not automatically merging stuff: it fosters frameworks which are used by many and thus make the ecosystem more consistent.

Dispatching to a specific method of the save function based on the input types is exactly what extending the function is. And knowing what save means is necessary in order to write generic code. If everyone has their own version of push! with arbitrary meaning, that means that whenever you see some code like

function g(x)
    for i in 1:100 
        push!(g, i)
    end
end

you have no idea what the purpose of it is.

Now you can just:

help?> push!
search: push! pushfirst! pushdisplay

  push!(collection, items...) -> collection

  Insert one or more items at the end of collection.
1 Like

If you read through that thread, you will notice some workarounds along the lines of

import A # has f
import B # has f

f(x::SomeType) = A.f(x) # specifically for SomeType
f(x) = B.f(x)           # fallback

and variations on this theme.

In general: Julia, like most contemporary generic programming languages, is very careful about namespace management. This may seem inconvenient in some cases, but I suppose that after writing a few 10⁴ LOC you will see its value.