Julian way of defining custom numeric types

Hello,

I am working with the DifferentialEquations package to play around some differential equations involving angles.

Instead of letting everything go on as a normal float and just afterwards reduce everything modulo 2pi for visualisation, i wanted to try to go the julian way and define a small wrapper for the Floag64 type

struct Angle <: Real
    x::Float64
end
Angle(x::Real) = Angle(x % 2pi)

Unfortunately this is not enough to make for a smooth addition to my program as julia struggles to convert and does not know what to do with this new type.

I know that i can extend all the base operations by hand, but i was left wondering if there is a smoother and easier way to let a simple primitive Number type wrapper just automatically fall in place the rest of the Julia implementation.

More in general i tried to look for some quick guidelines when creating a new custom type but i could not find one. Are there any Best Practices ™ when it comes to seamlessly embed a new custom type in the language?

Thanks!

1 Like

You should find an inspiration in manual describing the implementation of rational number.

https://docs.julialang.org/en/v1/manual/conversion-and-promotion/

Also, I think you can benefit from the macro @forward that I describe in this reply of mine.

1 Like

This is extremely interesting but the @forward macro seems to not retain the type as in

struct Angle <: Real
    x::Float64
end

@forward Angle.x Base.sqrt

then

typeof(Base.sqrt(Angle(1))) == Float64 #true

and that is not really what i need. Moreover, using forward on Base.:+ raises a MethodError anyway as there is ambiguity as the macro defines

+(x::Angle, args...; kwargs...)

I think i’ll stick the the “not lazy” version of embedding a custom type for now.

True, maybe eval would be more the case here.

for single_param_function in (:(Base.sqrt), :(Base.sin))
    eval(quote
        function $(single_param_function)(a :: Angle)
            return Angle($(single_param_function)(a.x))
        end
    end)
end

Someone with better knowledge of metaprogramming should check if this is reasonable (it works, but maybe is not ideal).

1 Like

Note that you will get more accurate results by using mod2pi(x) instead of x % 2pi (since the latter uses 2pi, a Float64 approximation of the actual number 2π).

4 Likes

Is the sine of an angle another angle or just a number? If you don’t need it to be an angle, then simply defining Base.float for your type will work for a lot of functions that use this as their fallback.

julia> Base.float(a::Angle) = a.x

julia> sin(Angle(1))
0.8414709848078965
2 Likes