Why is `promote_type` not applied by `div` with `Int` and `Rational`?

According to the docstring of promote_type:

promote_type represents the default promotion behavior in Julia when operators (usually mathematical) are given arguments of differing types.

And:

julia> promote_type(Int, Rational{Int})
Rational{Int64}

However, 1 ÷ (1//2) returns 2, not 2//1. In part it makes sense, because div always returns integers. But this is against the stated rules, isn’t it?

1 is promoted to 1//1 and

julia> (1//1) ÷ (1//2)
2

Looks consistent to me. Note that promotion only affects the inputs. What output type you get is up to the operator.

Also, promotion is the default. Specific methods are allowed to override this.

Often, this is done for performance reasons. For example, there is a specialized method for complex + real that works as if both arguments were promoted to the same complex type, but is faster (because it only needs to add the real parts).

It is consistent in the sense that div(::Rational{Int}, ::Rational{Int}) always produces an Int. But then, for two floats it returns a float:

julia> 1 ÷ 0.5
2.0

julia> 1 ÷ 0.5f0
2.0f0

I’d argue that it is still unexpected that sometimes div returns the same type as its arguments, sometimes not.

For even more nuance, div returns integers, but not necessarily with an Integer type e.g. 2.0 == div(2.5, 1). So after the totally separate promotion of the inputs to Rational{Int}, an output type of Rational{Int} could be reasonable for div. It just wasn’t chosen.

It’s a reasonable argument but it has nothing to do with promotion.