Custom conversion routines

module A

import Base: convert

export Foo

type Foo
    a::Float64
    b::Float64
end

function convert(::Type{Foo}, a::Tuple{Float64,Float64})
    Foo(a[1], a[2])
end
end

module B
using A

export Bar

type Bar
    x1 :: Foo
    x2 :: Foo
    function Bar(a::Foo, b::Foo) new(a,b) end
end
end

The test code in a separated file

using B

b = Bar((1.0,2.0), (1.0,2.0))
println(b)

ERROR: LoadError: MethodError: no method matching B.Bar(::Tuple{Float64,Float64}, ::Tuple{Float64,Float64})
Closest candidates are:
B.Bar{T}(::Any) at sysimg.jl:53

When I remove the internal constructor everything works as expected.

I was hoping somebody could explain why that is.

I can fix it by creating an external constructor which matches on Tuples.

Thank you.

1 Like

The auto conversion is done only in new not for function dispatch. So basically if you declared a type

type Bar
   x::Foo
end

You can pass a Tuple{Float64,Float64} to it’s default constructor or new used in your own inner constructor and the conversion will be done automatically. However, if you have a function foo(::Foo) it’ll not be able to accept a Tuple{Float64,Float64} object, inner constructor included. If you want to accept a wider range of types and do conversion, then just specify that in the signature (i.e. remove the type constraint on it).

P.S. Auto conversion is also done on assignment to typed slot. Mentioning this mainly for completion and not directly related to this issue.

Thanks for you help.

I changed

function Bar(a::Foo, b::Foo) new(a,b) end

to

function Bar(a, b) new(a,b) end

and that works.

I guess I’m surprised that the type signature breaks it.

Why doesn’t the compiler look at the fact that it needs a Foo, got a tuple, and goes off and looks for a conversion/promotion ? Or maybe the problem is I need promote instead of convert ?

Because that’s not how dispatch works. Given the number of convertion methods defined, it’ll be extremely ambiguous in general which one should actually be called. (e.g. If you also have a convertion from NTuple{2,Float64} to Bar and have another method Bar(::Bar, ::Bar), which one do you want to call?)

It’s probably not impossible to come up with a set of rules to determine what is ambiguous and what’s not but given the “non-locality” of the information (i.e. it’s stored both in the method table of the method you are calling as well as the whole set of convertion rules, possibly chained) it’ll likely be very hard to predict and will certainly be extremely hard to implement dynamically with any reasonable performance.