So I am learning Julia from Erik Engheim’s book “Julia for Beginners”. I am working through the code as I read the book (I put all my work in Github if anyone else is using the book and want to compare their own code)
He has a chapter on defining angle types as a way to teach type conversion rules and promotion (which is quite a nice feature of Julia). Unfortunately, my code isn’t working as expected and I’m not sure why. Here is the code:
abstract type Angle end
struct Radian <: Angle
radians::Float64
end
struct DMS <: Angle
seconds::Int
end
begin
Degree(degrees::Integer) = Minute(degrees * 60)
Degree(deg::Integer, min::Integer) = Degree(deg) + Minute(min)
Degree(deg::Integer, min::Integer, secs::Integer) = Degree(deg, min) + Second(secs)
function Minute(minutes::Integer)
DMS(minutes * 60)
end
function Second(seconds::Integer)
DMS(seconds)
end
import Base: -, +, show, convert, *, /, promote_rule
+(Θ::DMS, α::DMS) = DMS(Θ.seconds + α.seconds)
-(Θ::DMS, α::DMS) = DMS(Θ.seconds - α.seconds)
+(Θ::Radian, α::Radian) = Radian(Θ.radians + α.radians)
-(Θ::Radian, α::Radian) = Radian(Θ.radians - α.radians)
function degrees(dms::DMS)
minutes = dms.seconds ÷ 60
minutes ÷ 60
end
function minutes(dms::DMS)
minutes = dms.seconds ÷ 60
minutes % 60
end
seconds(dms::DMS) = dms.seconds % 60
function show(io::IO, dms::DMS)
print(io, degrees(dms), "° ", minutes(dms), "' ", seconds(dms), "''")
end
function show(io::IO, rad::Radian)
print(io, rad.radians, "rad")
end
# convert constructors
Radian(dms::DMS) = Radian(deg2rad(dms.seconds/3600))
DMS(rad::Radian) = DMS(floor(Int, rad2deg(rad.radians) * 3600))
convert(::Type{Radian}, dms::DMS) = Radian(dms)
convert(::Type{DMS}, rad::Radian) = DMS(rad)
sin(rad::Radian) = Base.sin(rad.radians)
cos(rad::Radian) = Base.cos(rad.radians)
sin(dms::DMS) = sin(Radian(dms))
cos(dms::DMS) = cos(Radian(dms))
*(coeff::Number, dms::DMS) = DMS(coeff * dms.seconds)
*(dms::DMS, coeff::Number) = coeff * dms
/(dms::DMS, denom::Number) = DMS(dms.seconds/denom)
*(coeff::Number, rad::Radian) = Radian(coeff * rad.radians)
*(rad::Radian, coeff::Number) = coeff * rad
/(rad::Radian, denom::Number) = Radian(rad.radians/denom)
const ° = Degree(1)
const rad = Radian(1)
Base.promote_rule(::Type{Radian}, ::Type{DMS}) = Radian
end
As can be seen I have a promote rule and this works for me:
+(promote(90°,3.14rad/2)...)
# result:
3.140796326794897rad
But it I just do a +
without the explicit promote
it complains. I don’t understand why it isn’t automatically promoting the DMS to Radian:
90°+ 3.14rad/2
# result:
ERROR: MethodError: no method matching +(::DMS, ::Radian)
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
+(::DMS, ::DMS) at /Users/aronet/Code/FourM/Study/Julia/JuliaforBeginners/angleunits.jl:25
+(::Radian, ::Radian) at /Users/aronet/Code/FourM/Study/Julia/JuliaforBeginners/angleunits.jl:28
Stacktrace:
[1] top-level scope
@ REPL[5]:1
Thanks for any help.