# ANN: Convertible.jl - Instant multi-step convert

**URL:** <https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384>\
**Category:** Community\
**Tags:** package, announcement\
**Created:** [March 1, 2017, 4:14pm UTC](https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384 "2017-03-01T16:14:22Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![helgee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/helgee/32/2022_2.png) [@helgee](https://discourse.julialang.org/u/helgee)\
**Post date:** [March 1, 2017, 4:14pm UTC](https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384/1 "2017-03-01T16:14:22Z")

</div>

Hi, everyone,

I proudly present [Convertible.jl](https://github.com/helgee/Convertible.jl). The package provides the `isconvertible` trait that can be applied to type definitions via the `@convertible` macro. Types that share this trait can easily be converted into one another with a single call to `Base.convert` even though multiple intermediate conversions might be required.

For those of you who have seen my [JuliaCon talk](https://www.youtube.com/watch?v=IjJqVwtWO3s) last year, this is a less dirty and generalised version of the type-system-exploiting conversion trick I presented there.

### Example

Define convertible types:

```julia
@convertible struct A
    val::Int
end

@convertible struct B
    val::Int
end

@convertible struct C
    val::Int
end

```

Define `convert` methods:

```julia
Base.convert(::Type{B}, a::A) = B(a.val+1)
Base.convert(::Type{C}, b::B) = C(b.val+1)

```

Convert… (you need to opt-in to this behaviour by wrapping the expression with `@convert` )  
Internally Convertible.jl will compute the shortest conversion path and emit a specialized method based on a generated function, e.g. `convert(C, convert(B, a))` in this case.

```julia
julia> a = A(1)
julia> @convert convert(C, a)
C(3)

```

Profit!

---

<div class="post-metadata">

**Author:** ![Michael\_Eastwood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michael_eastwood/32/669_2.png) [@Michael\_Eastwood](https://discourse.julialang.org/u/Michael_Eastwood)\
**Post date:** [March 1, 2017, 6:34pm UTC](https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384/2 "2017-03-01T18:34:28Z")

</div>

This is really cool!

I wonder if it would be possible for Convertible.jl to automatically define the missing convert methods instead of requiring the user to opt-in to the behavior.

I’m imagining a use case where I have ~10 different types that should all be convertible between each other and I don’t want to burden the user with using a macro to do the conversions (I’d like `convert` to just work). It would be neat if I could simply define a minimal number of `convert` methods and use Convertible.jl to define the missing ones.

---

<div class="post-metadata">

**Author:** ![helgee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/helgee/32/2022_2.png) [@helgee](https://discourse.julialang.org/u/helgee)\
**Post date:** [March 1, 2017, 7:38pm UTC](https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384/3 "2017-03-01T19:38:37Z")

</div>

The package originally worked this way (requiring just `@convertible`) but @tkelman asked me to change it. His objection was that changing the behaviour of a `Base` method upon import of a package isn’t very good style and might lead to subtle breakage in other people’s code. A valid point in my opinion.

In my packages I tend to hide the call to `convert` from the users anyways. The user is supposed to use type constructors instead. Like this:

```julia
abstract type Blob end

@convertible struct Foo <: Blob end
@convertible struct Bar <: Blob end
@convertible struct FooBar <: Blob end

convert(::Type{Bar}, ::Foo) = Bar()
convert(::Type{FooBar}, ::Bar) = FooBar()

(::Type{T}){T<:Blob, S<:Blob}(s::S) = @convert convert(T, s)

```

and then the user calls

```julia
f = Foo()
fb = FooBar(f)

```

and never sees the `@convert` macro.

---

<div class="post-metadata">

**Author:** ![helgee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/helgee/32/2022_2.png) [@helgee](https://discourse.julialang.org/u/helgee)\
**Post date:** [March 1, 2017, 8:10pm UTC](https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384/4 "2017-03-01T20:10:30Z")

</div>

I just realised that my example above does not work 🙈

But I fixed the bug and a new release is on its way to METADATA.

---

<div class="post-metadata">

**Author:** ![Michael\_Eastwood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michael_eastwood/32/669_2.png) [@Michael\_Eastwood](https://discourse.julialang.org/u/Michael_Eastwood)\
**Post date:** [March 1, 2017, 9:45pm UTC](https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384/5 "2017-03-01T21:45:17Z")

</div>

Hmm. That’s not quite what I had in mind.

```julia
@convertible struct A end
@convertible struct B end
@convertible struct C end

convert(::Type{B}, ::A) = B()
convert(::Type{C}, ::B) = C()

@define_convert_method A C
# expands to: convert(::Type{C}, x::A) = convert(C, convert(B, x))

```

I think if you do it this way you’re not changing the behavior of `convert` when Convertible.jl is imported. Rather it’s just helping you define a bunch of additional methods for `convert`.

---

<div class="post-metadata">

**Author:** ![helgee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/helgee/32/2022_2.png) [@helgee](https://discourse.julialang.org/u/helgee)\
**Post date:** [March 3, 2017, 6:25am UTC](https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384/6 "2017-03-03T06:25:37Z")

</div>

Interesting.

I think I would also have something like `@generate_convert` without arguments that generates the missing methods for all permutations if you have many convertible types.

Your approach shifts the costs from run-time to compile-time which might be good for many applications.

---

<div class="post-metadata">

**Author:** ![Michael\_Eastwood](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/michael_eastwood/32/669_2.png) [@Michael\_Eastwood](https://discourse.julialang.org/u/Michael_Eastwood)\
**Post date:** [March 3, 2017, 7:28am UTC](https://discourse.julialang.org/t/ann-convertible-jl-instant-multi-step-convert/2384/7 "2017-03-03T07:28:20Z")

</div>

Of course after writing that I realized I can get what I want with

```julia
convert(::Type{C}, x::A) = @convert convert(C, A)

```

which isn’t a whole lot more typing than `@define_convert_method A C`.

The macro that generates all the missing permutations is probably more useful than my idea.
