const DEGREE = pi/180
sin(90DEGREE)
introduces the possibility of one ULP error in the calling arg. Not
big, admittedly, but try doing sind(180) vs. sin(pi) and see what the
results are.
julia> sind(30)
0.49999999999999994
module DegreeModule
export Degree, DegreeSign, °
immutable Degree{T<:Number} <:Number
d::T
end
immutable DegreeSign
filler::Bool
end
const ° = DegreeSign(true)
*(num::Number, s::DegreeSign) = Degree(num)
Base.sin(d::Degree) = sinpi(d.d/180)
end
using DegreeModule
Degree(125) # ==> Degree{Int64}(125)
130° # ==> Degree{Int64}(130)
sin(180°) # ==> 0.0
Base.sin(x::Radians) = sin(val(x))
Base.sin(x::Degrees) = sind(val(x))
Base.sin(x::Angle) = sin(Radians(x))
module Trigonometry #Probably excessive
...
module MatlabCompatibility
...
end
end
module Algebra #Probably excessive
...
module MatlabCompatibility
...
end
end
using Trigonometry
using Algebra
using MatlabCompatibility
abstract Unit
val(x::Unit) = x.v
abstract Angle <: Unit
immutable type Radians{T<:Number} <: Angle
v::T
end
unitlessVal = val(Radians(pi))
The real question is how much programming overhead is required to use these types (assuming the compiler does the grunt work reducing the *performance* overhead)
Then again, using type wrappers for this – bare numbers are Radians while an immutable Degree wrapper could wrap values in degrees – would eliminate a large class of common programming errors when working with angles.
Since types should be used sparingly, I don't think it a good idea to
replace convenience functions like sind and cosd with a type-driven
invocation mechanism -- ordinary users will be confused.