Hi,
I want to implement
Base.+(x::Float32,y::Float32)
differently in module A, and module B.
In module C, depending on which module (A or B) I use,I want the proper + function to be called (the one in A or B)?
Is there a way to do this?
Hi,
I want to implement
Base.+(x::Float32,y::Float32)
differently in module A, and module B.
In module C, depending on which module (A or B) I use,I want the proper + function to be called (the one in A or B)?
Is there a way to do this?
You can’t. Since it is the same method, the most recent implementation will overwrite.
Also, you should probably not do this at all.
Perhaps you could explain what you want to achieve? Eg a wrapper type would allow you to define your custom Base.+
.
how can i do it with a wrapper type?
THanks
Something like
struct Wrapper{T}
value::T
end
Base.+(a::Wrapper{Float32}, b::Wrapper{Float32}) =
Wrapper{Float32}(a.value + b.value) # modify this for custom behavior
It’s actually not impossible, this is what I created the ForceImport package for,
module Foo
export +
+() = 7
end
module Bar
using ForceImport
@force using Foo
end
julia> Bar.:+()
7
Currently being registered in Metadata.
Additionally, since people might not know how powerful the @force
is, you can safely extend +
locally too
julia> module Foo
export +
+(r...) = Base.:+(r...)
+(a::Float32,b::Float32) = "Float32 +"
end
Foo
julia> module Bar
using ForceImport
@force using Foo
println(1.0+sqrt(2))
println(Float32(1.0)+Float32(sqrt(2)))
end;
2.414213562373095
Float32 +
As you can see, you can safely extend the Base
definition of +
and replace the dispatch for Float32
, and still retain the dispatch for all the other types on +
. The @force
is very powerful indeed.
Sorry if I missed something about ForceImport, but are you sure this will implement two different methods for the same signature, which is what @Qiyamah asked for?
As I understood it, he wants to define multiple versions of +
in various modules. Obviously, he cannot import the +
of both of those modules in the same package. However, with ForceImport
, you can indeed make different definitions of +
in different modules, and then be able to use it in another package.
@Qiyamah will have to clarify what exactly he was trying to do. If he is really trying to import the redefined method from two different packages in a single scope, and they are both the same signature, then it’s not possible without prefixing the call with the module name. What I proposed is for the situation, if he wanted to replaced the Float32
definition of +
in some module, and then use it in another module. You could have multiple modules each defining +
differently for Float32
, but you could only import one of them per module.
You can already do that with qualified names A.+
and B.+
or with an explicit import A.+
statement (if you only need either A or B but not both) without ForceImport, as you well know. ForceImport just does the latter silently.
The real question here is what the original poster is actually trying to accomplish. @Qiyamah, don’t tell us the means you want to employ, tell us the end.
This is what I am trying to do.
module MyModule
export Ding,foo,DD
abstract type Ding{T} end;
struct DD<:Ding{Float64}
x::Float64
end
function foo(x::Ding{Float64})
(x+x)+x
end
end
module PlusC1
using MyModule
struct C1
#stuff
end
const inst = C1()
Base.(:+)(x::Ding{Float64},y::Ding{Float64}) = plus(x,y,inst)
function plus(x::Ding{T},y::Ding{T},c::C1) where T
println("C1")
x
end
end
module PlusC2
using MyModule
struct C2
#stuff
end
const inst = C2()
Base.(:+)(x::Ding{Float64},y::Ding{Float64}) = plus(x,y,inst)
function plus(x::Ding{T},y::Ding{T},c::C2) where T
println("C2")
x
end
end
module Test1
using MyModule
using PlusC1
foo(DD(21.1)) # should print C1\n C1\n
end
module Test2
using MyModule
using PlusC2
foo(DD(21.1)) # should print C2\n C2\n
end
As you see, the + operation in foo must be bound according to the module i am importing which defines + in a new fashion.
This is the problem i am trying to solve,
Thanks
You are still explaining the means and not the end. What is the end goal?
If you’re really dead set on using +
syntax, you might write macro first_plus
that rewrites a + b
to first_plus(a, b)
in the first module and macro second_plus
which rewrites it to second_plus(a, b)
in the second module.
Thanks, that i already thought , but i am asking whether there may be a multidispatch way without even involving the macros. that macro is not the best idea, if this module, do this, if that module do that
Another option is to just use a different unicode symbol (which is easy with tab completion), such as ⊞
(available at the REPL via \boxplus
-TAB).
Out of curiosity, what language are you primarily coming from? Just trying to establish priors and worldview.
The confusion I have with your example is that you seem to be doing two slightly different things:
plus
function defined in 2 different modules, operating on 2 different types. This seems perfectly reasonable to me, as there is no ambiguity and they have different signatures. In C++ or something like that, you could just keep the functions in the appropriate module and it wouldn’t be an issue (even if you use import
instead of using
). However, in Julia there is no ADL so to get this to work you need to combine things in a single namespace. For things like +
people put it in Base
but for other function names you might just need to fudge around with the modules.(:+)(x::Ding{Float64},y::Ding{Float64})
) do different things. This doesn’t work very well in Julia, where you have to put the (:+)
methods in Base
, but I am suspicious that ADL may not really help your design much here.C++ guy here!
Basically you are telling me that i can’t choose which :+ method applies within a module, because there is only one namespace.
Then julia provides a limited OO experience. and compensate for it with macros.
This doesn’t work either:
module Arith
export Rep, DD
abstract type Rep{T} end
struct DD{T<:Float64} <: Rep{T}
x::T
end
Base.:+(x::Rep{Float64},y::Rep{Float64}) = plus(x,y)
end
module P1
using Arith
export plus
plus(x::DD, y::DD) = DD(x.x+y.x)
end
module P2
using Arith
export plus
plus(x::DD,y::DD) = DD(x.x-y.x)
end
module X1
using Arith
using P1
DD(4.2) + DD(4.3)
end
module X2
using Arith
using P2
DD(4.2) + DD(4.3)
end
It says
ERROR: UndefVarError: plus not defined
Stacktrace:
[1] +(::Arith.DD{Float64}, ::Arith.DD{Float64}) at ./REPL[1]
So how can i selectively bind :+ within a module?
Thanks
plus
is not defined in the scope of Arith
so you get the UndefVarError
.
No, there is not only one namespace. In fact, the reason why you get your error is because there are mutliple namespaces.
Well, julia is not an object-oriented language. But there is nothing to compensate for, multiple dispatch is in my opinion in most cases cleary superior over OO.
I wouldn’t say that, and I don’t think this has anything to do with OO. The issue you are having has to do with your mental model of how to look up the valid methods for a given function name. What I would say is that there is a worldview clash between people coming from C++/Java/Python/C#/etc. and Lisp - where many of the core developers using Julia come from a Lisp mindset.
If you come from an OO (i.e. single-dispatch) world it is immediately obvious that the namespace of the type provides valid methods to operate on that type (hence, you can always keep operator+
in the namespace of the type it applies to in any OO language), and if you come from function overloading (i.e. C++) then there is ADL which means the compiler looks in the namespace of any of the types in the signature of the function.
If you read through the later parts of Function name conflict: ADL / function merging? you will finally see that we figured out why namespaces in Julia are so confusing to people from C++/Java/Python/C# and make complete sense to people who used CLOS. Hopefully there will be a way to reconcile the worlds.
In principle you could put a :+ wherever you want. In practice you are going to have to put it in the Base
module. As for other functions, if you want to easily use the same function name on completely different type signatures, then you generally have to find a shared namespace to put them in. Finally, if you want to use any of the function names that are used in Base
for your own types, then you probably want to just put those functions in Base
to make your life easier. It may be ugly, but it is a reasonable medium-term solution… (and longterm solution if the Lisp-style thinking on function lookup is maintained).
But, in your exact example, I am not sure if a C+±style ADL approach would work either (at least for the operator+ part. The plus
part would be in C++ fine since the signatures operate on distinct types.
This has nothing to do with OO, nor does it exclude multiple-dispatch. It is just that people who come from an OO language expect there to be some sort of argument dependent lookup of valid methods by looking at the namespace of the type it is called on (even if they don’t realize that is how single-dispatch languages work).
Keep in mind that in C++, and other languages with function overloading, it is kind of like multiple-dispatch with the type is known at compile-time. Eventually, C++ may also even have something that does that dynamically (http://www.stroustrup.com/multimethods.pdf), just like in julia. The name lookup rules are orthogonal to all of this (even if the implementation can get tricky in a more dynamic language).
That’s because you never defined what plus
is in the Arith
module. You can do it like this:
module Arith
export Rep, DD
abstract type Rep{T} end
struct DD{T<:Float64} <: Rep{T}
x::T
end
end
module P1
using Arith
export +
+(r...) = Base.:+(r...)
+(x::DD, y::DD) = DD(x.x+y.x)
end
module P2
using Arith
export +
+(r...) = Base.:+(r...)
+(x::DD, y::DD) = DD(x.x-y.x)
end
module X1
using Arith, ForceImport
@force using P1
DD(4.2) + DD(4.3)
end
module X2
using Arith, ForceImport
@force using P2
DD(4.2) + DD(4.3)
end
That shouldn’t give you an error, the problem is you had a plus
function that wasn’t defined in Arith
.
You still haven’t explained what you’re trying to accomplish. All you’ve done is posted meaningless toy snippets. (I don’t want you to post production code, I want you to describe what your code is really trying to do.)
I have a feeling that you’re trying to shoehorn some completely foreign abstraction into Julia that would be implemented in an entirely different way by an experienced Julia programmer.