Method that acts on union types, what is wrong?

What is wrong with this definition of a method that is thought to work on both types defined?

julia> struct A end

julia> struct B end

julia> function test( x :: T, y :: T ) where T <: Union{A,B}
         true
       end
test (generic function with 1 method)

julia> a = A() ; b = B();

julia> test(a,b)
ERROR: MethodError: no method matching test(::A, ::B)
Closest candidates are:
  test(::T, ::T) where T<:Union{A, B} at REPL[84]:1
Stacktrace:
 [1] top-level scope at REPL[86]:1

1 Like

Ok, it is expecting that the type is the same for both variables. This works:

julia> function test( x :: T1, y :: T2 ) where {T1 <: Union{A,B}, T2 <: Union{A,B}}
         true
       end
test (generic function with 3 methods)

julia> test(a,b)
true


2 Likes

If you don’t need the type parameter inside the function body, then you could just write that as this:

julia> function test(x::Union{A,B}, y::Union{A,B})
           true
       end
test (generic function with 1 method)

julia> test(a, b)
true
4 Likes

No, I do not need it. But in my case we are talking of

Union{ComplexMixtures.Result,ComplexMixtures.Density,ComplexMixtures.Volume}

writing this more than once is not fun. I guess nothing is wrong on using :: T, wright?

By the way, I am just writing an isapprox function that compares every field of a struct. Is there already a base function for that?

function isapprox( r1 :: T, r2 :: T ) where T <: Union{ComplexMixtures.Result,ComplexMixtures.Density,ComplexMixtures.Volume}
  check = true
  for field in fieldnames(typeof(r1))
    x = getfield(r1,field)
    y = getfield(r2,field)
    if ! (x ≈ y)
      check = false
      println(" Data in $field field differ. ")
    end
  end
  return check
end

Edit: Rs, I just realized that I do need the type :slight_smile:, I was just using typeof instead :grimacing:

1 Like

why not define in your module:

const AMixture = Union{ComplexMixtures.Result,ComplexMixtures. ...}

Then you can use AMixture in your definitions and Julia expands it to the longer term.

2 Likes

Yes, sure. Yet for the moment I only need that Union in exactly that point. That test function is the only requiring this type of input.

If you haven’t overloaded getproperty, you could use propertynames, which accepts an instance rather than a type:

propertynames(r1)

Also, do you need to do an intersection of the field/property names for r1 and r2?

By the way, that’s completely fine if it makes your code easier to write or read or maintain. You are free to write:

function foo(x)
  println("the type of x is: ", typeof(x))
end

instead of

function foo(x::T) where {T}
  println("the type of x is: ", T)
end

and you should expect to see little or no difference in the performance of your code (in fact, I suspect the compiler will generate exactly the same code in most cases). After all, if the compiler has access to the type of x, then it can insert the answer to typeof(x) without requiring any work to be done at runtime.

4 Likes

Oh, cool, I didn’t know about that.

I am not sure if I understand the question. I am actually comparing two instances of the same structs, and I want to be sure that all the data contained in them is the same (the result of a test case computation of the package). These structs contain other structs inside (of those 4 types of my program), such that that isapprox function runs for all the fields of everything.

Ah, I think now I understand your question: no, no, the fields are the same. If the structs where different of course that loop would have to be on the common fields of both structures only.

The original question was, actually, a result of me not knowing how the parametric type worked, because I was adding manually an error message to stop the execution if the two types were different. Thus, what I actually wanted was ( x :: T, y :: T ) where T <: Union{A,B}, which will throw an error if the types are different.

1 Like