The following code is from the Julia manual. My question is below.
julia> struct OurRational{T<:Integer} <: Real
num::T
den::T
function OurRational{T}(num::T, den::T) where T<:Integer
if num == 0 && den == 0
error("invalid rational: 0//0")
end
num = flipsign(num, den)
den = flipsign(den, den)
g = gcd(num, den)
num = div(num, g)
den = div(den, g)
new(num, den)
end
end
julia> OurRational(n::T, d::T) where {T<:Integer} = OurRational{T}(n,d)
OurRational
julia> OurRational(n::Integer, d::Integer) = OurRational(promote(n,d)...)
OurRational
julia> OurRational(n::Integer) = OurRational(n,one(n))
OurRational
julia> ⊘(n::Integer, d::Integer) = OurRational(n,d)
⊘ (generic function with 1 method)
julia> ⊘(x::OurRational, y::Integer) = x.num ⊘ (x.den*y)
⊘ (generic function with 2 methods)
julia> ⊘(x::Integer, y::OurRational) = (x*y.den) ⊘ y.num
⊘ (generic function with 3 methods)
julia> ⊘(x::Complex, y::Real) = complex(real(x) ⊘ y, imag(x) ⊘ y)
⊘ (generic function with 4 methods)
julia> ⊘(x::Real, y::Complex) = (x*y') ⊘ real(y*y')
⊘ (generic function with 5 methods)
julia> function ⊘(x::Complex, y::Complex)
xy = x*y'
yy = real(y*y')
complex(real(xy) ⊘ yy, imag(xy) ⊘ yy)
end
⊘ (generic function with 6 methods)
As a consequence of those definitions the manual gives this example
julia> z = (1+2im)⊘(1-2im);
Julia> typeof(z)
Complex{OurRational{Int64}}
Which works fine ... unless you leave off the ';'
julia> z = (1+2im)⊘(1-2im)
OurRational{Int64}(-3, 5)Error showing value of type Complex{OurRational{Int64}}:
ERROR: promotion of types OurRational{Int64} and Int64 failed to change any arguments
Stacktrace:
[1] error(::String, ::String, ::String)
@ Base ./error.jl:44
[2] sametype_error(input::Tuple{OurRational{Int64}, Int64})
@ Base ./promotion.jl:417
[3] not_sametype(x::Tuple{OurRational{Int64}, Int64}, y::Tuple{OurRational{Int64}, Int64})
@ Base ./promotion.jl:411
[4] promote
@ ./promotion.jl:394 [inlined]
[5] <(x::OurRational{Int64}, y::Int64)
@ Base ./promotion.jl:462
[6] signbit(x::OurRational{Int64})
I've no idea why. If you use the builtin Rational operator
julia> z = (1+2im)//(1-2im)
No problems.
Any idea?
The underlying issue is that OurRational{Int}
and Int
are incompatible types: an Int
cannot be converted into an OurRational{Int}
. Inspecting the stack trace will show this:
OurRational{Int64}(-3, 5)ERROR: promotion of types OurRational{Int64} and Int64 failed to change any arguments
Stacktrace:
[1] error(::String, ::String, ::String)
@ Base ./error.jl:44
[2] sametype_error(input::Tuple{OurRational{Int64}, Int64})
@ Base ./promotion.jl:417
[3] not_sametype(x::Tuple{OurRational{Int64}, Int64}, y::Tuple{OurRational{Int64}, Int64})
@ Base ./promotion.jl:411
[4] promote <-- issue here ********
@ ./promotion.jl:394 [inlined]
[5] <(x::OurRational{Int64}, y::Int64)
@ Base ./promotion.jl:462
[6] signbit(x::OurRational{Int64})
@ Base ./number.jl:137
[7] show(io::IOContext{Base.TTY}, z::Complex{OurRational{Int64}})
@ Base ./complex.jl:201
[8] show(io::IOContext{Base.TTY}, ::MIME{Symbol("text/plain")}, x::Complex{OurRational{Int64}})
@ Base.Multimedia ./multimedia.jl:47
<snip>
To fix this, define:
Base.promote_rule(::Type{OurRational{T}}, ::Type{S}) where {T,S} =
OurRational{promote_type(T, S)}
And now…
OurRational{Int64}(-3, 5)ERROR: MethodError: no method matching OurRational{Int64}(::Int64)
Closest candidates are:
OurRational{T}(::T, ::T) where T<:Integer
@ Main ~/Offline-Documents/Scratchpad/f.jl:5
(::Type{T})(::T) where T<:Number
@ Core boot.jl:792
(::Type{T})(::Complex) where T<:Real
@ Base complex.jl:44
...
Stacktrace:
<snip>
Ok, let's define that then:
OurRational{T}(n::T) where {T<:Integer} = OurRational(n)
Not quite there…
OurRational{Int64}(-3, 5)ERROR: < not defined for OurRational{Int64}
Stacktrace:
<snip>
One more try:
# actual implementation left as exercise
Base.:(<)(x::OurRational{S}, y::OurRational{T}) where {S,T} = x.num//x.den < y.num//y.den
Success!
julia> z = (1 + 2im) ⊘ (1 - 2im)
OurRational{Int64}(-3, 5) + OurRational{Int64}(4, 5)*im