Promotion rules - what about when you can't promote?

question

#1

I’m writing some code for the first time dealing with promotion in julia.

Specifically I’m trying to defining some promotion rules for Bio.jl, first a bit of background:

BioJulia defines the following alphabet types:

"""
Alphabet of biological characters.
"""
abstract Alphabet

"""
DNA nucleotide alphabet.
"""
immutable DNAAlphabet{n} <: Alphabet end

"""
RNA nucleotide alphabet.
"""
immutable RNAAlphabet{n} <: Alphabet end

I defined the rules like so:

for alph in (DNAAlphabet, RNAAlphabet)
    @eval function Base.promote_rule{A<:$alph,B<:$alph}(::Type{A}, ::Type{B})
        return $alph{max(bitsof(A),bitsof(B))}
    end
end

So when you have two of the same alphabet (e.g. DNAAlphabet), but with different n, then promote_rule will return the alphabet with the higher n e.g. promote_rule(DNAAlphabet{4}, DNAAlphabet{2}) == DNAAlphabet{4}

This is because DNAAlphabet{4} is a larger alphabet containing all the elements of the smaller DNAAlphabet{2}.

So I then define a promote rule for the sequences that use those alphabets:

for alph in (DNAAlphabet, RNAAlphabet)
    @eval function Base.promote_rule{A<:$alph,B<:$alph}(::Type{BioSequence{A}}, ::Type{BioSequence{B}})
        return BioSequence{promote_rule(A,B)}
    end
end

a = BioSequence{DNAAlphabet{2}}("aaaaa")
b = BioSequence{DNAAlphabet{4}}("aaaaa")
c = BioSequence{RNAAlphabet{2}}("aaaaa")
d = BioSequence{RNAAlphabet{4}}("aaaaa")

Now, using promotion works:

julia> typeof(promote(a, b))
Tuple{Bio.Seq.BioSequence{Bio.Seq.DNAAlphabet{4}},Bio.Seq.BioSequence{Bio.Seq.DNAAlphabet{4}}}

julia> typeof(promote(c, d))
Tuple{Bio.Seq.BioSequence{Bio.Seq.RNAAlphabet{4}},Bio.Seq.BioSequence{Bio.Seq.RNAAlphabet{4}}}

If there isn’t a promote_rule for the combination of alphabets, promote does no conversion:

julia> typeof(promote(a, d))
Tuple{Bio.Seq.BioSequence{Bio.Seq.DNAAlphabet{2}},Bio.Seq.BioSequence{Bio.Seq.RNAAlphabet{4}}}

Actually in BioJulia you can convert a BioSequence{RNAAlphabet} to a BioSequence{DNAAlphabet}, but it should be a conscious choice and deliberate action to do so, and it should not just be done by a function implicitly through promotion.

So, what is the best thing to do? Does I leave this behaviour as is - where promote doesn’t do any conversion in this case?

Or should some rules be added to do some kind of throw ~“Something’s trying to promote a DNA sequence into an RNA one”?

I assume one can make throws happen during promote for certain argument types as the docs for promote say:

Promotion to a common “greater” type is performed in Julia by the promote function, which takes any number of arguments, and returns a tuple of the same number of values, converted to a common type, or throws an exception if promotion is not possible.

Thanks,
Ben.


#2

This seems fine to me. Unlike, say, convert, promote isn’t meant to be called by users directly, and it doesn’t have a contract that says it must return values of the same type. You can use promote to make the Bio.jl interface more convenient but shouldn’t assume it will have any effect.

This also happens with e.g.

julia> promote(1, :a)
(1, :a)

#3

Ok, cool! Thanks Mike!