Unions vs adding methods

I just read up on Unions, and I get the impression that approach A and B below would be exactly equivalent:

int32_only_eater(i::Int32) = println(i)

# Approach A
eatint(i::Int32) = int32_only_eater(i)
eatint(i::Int64) = int32_only_eater(convert(Int32,i))
eatint(i::Float64) = int32_only_eater(convert(Int32,i))

# Approach B
typealias IntUnion Union{Int32,Int64,Float64}
eatint2(i::IntUnion) = int32_only_eater(convert(Int32,i))

Is that correct? If so, I have been making my life needlessly complicated by using approach A.

2 Likes

Not (currently) for ambiguity resolution purpose.

1 Like

And for possible change of ambituity detection in the future, it’s a matter of whether f(::Union{A,B},::A) and f(::A,::Union{A,B}) should be an method redefinition warning or an ambiguity.

1 Like

I understand the ambiguity in this example, but in cases where there is no ambiguity isn’t it the same as writing out all permutations of arguments? (this seems strangely circular, but I hope you know what I mean)

In particular, for the 1-argument example in the original post, aren’t the two approaches the same?

That’s what I said. They are not the same for the purpose of ambiguity detection and therefore dispatch. You can get ambiguity with only a single argument without any problem.

julia> f(::Union{Int,Float32}) = 1
f (generic function with 1 method)

julia> f(::Union{Int,Float64}) = 2
f (generic function with 2 methods)

julia> f(1)
ERROR: MethodError: f(::Int64) is ambiguous. Candidates:
  f(::Union{Float64, Int64}) in Main at REPL[2]:1
  f(::Union{Float32, Int64}) in Main at REPL[1]:1

A quick check with @code_native seems to indicate they generate the same code at least. I think in my application the ambiguities are not really an issue, it’s mostly in the context of CxxWrap to make it easier to pass things like unsigned integers to C++ (i.e. without asking users to put UInt() around arguments) and also to allow passing an object and its smart pointer to the same function. Currently I have code that indeed generates all permutations for all requested overloads, so I was hoping to simplify this using Unions.

Well, the signature you specify only affect dispatch and only in very rare cases affect code generation (Function and Type) so in that sense, nothing you put there affect specialization and Union is totally irrelevant here.

And of course they affect dispatch more than just ambiguity detection because it can affect invoke too for the same reason.

julia> invoke(f, Tuple{Union{Int,Float32}}, 1)
1

Yes, I shouldn’t have said “1-argument”, but rather the circular-sounding “when there are no ambiguities”.
I was interpreting the original question as whether these are identical:

f(::Union{Int,Float32}) = 1

and

f(::Int) = 1
f(::Float32) = 1

As I said

  • For performance it doesn’t matter whatever you put in the signature. Whether it is Union or not.
  • For dispatch, it matters for certain cases and doesn’t matter for other cases.
    If your question is if it doesn’t matter for the cases it doesn’t matter than the answer is obviously yes. My two comments above are just pointing out the cases that I can think of where it does matter.

OK, I think I finally get it. Thank you.

As you said, “method redefinition warning or an ambiguity”.

The following was instructive for me:

julia v0.5.0> f(::Union{Int,Float32}) = 1
f (generic function with 1 method)

julia v0.5.0> f(::Union{Int,Float64}) = 2
f (generic function with 2 methods)

julia v0.5.0> f(1)
ERROR: MethodError: f(::Int64) is ambiguous. Candidates:
  f(::Union{Float64,Int64}) at REPL[2]:1
  f(::Union{Float32,Int64}) at REPL[1]:1

Separate definitions:

julia v0.5.0> f(::Int) = 1
f (generic function with 1 method)

julia v0.5.0> f(::Float32) = 1
f (generic function with 2 methods)

julia v0.5.0> f(::Int) = 2
WARNING: Method definition f(Int64) in module Main at REPL[1]:1 overwritten at REPL[3]:1.
f (generic function with 2 methods)

julia v0.5.0> f(::Float64) = 2
f (generic function with 3 methods)

julia v0.5.0> f(1)
2

OK, thanks for the replies, as usual things aren’t as simple as they appear on the surface :slight_smile: I conclude that for CxxWrap what currently results in a method redefinition warning will become an ambiguity error if I switch to Unions. Since redefinition warnings here indicate that a wrapped C++ function is masked by another, they also must be treated as errors, so I can safely switch to the Union approach.