Best way to express this algebra between Types

struct Perturbator
    rng::AbstractRNG
end

function (a::Perturbator)()
    randn(a.rng)
end

const MultiLike = Union{Tuple,Array}

+′(a,b) = a + b
-′(a,b) = a - b
*′(a,b) = a * b
/′(a,b) = a / b

+′(a::T,b) where T <: MultiLike = a .+′ b
-′(a::T,b) where T <: MultiLike = a .-′ b
*′(a::T,b) where T <: MultiLike = a .*′ b
/′(a::T,b) where T <: MultiLike = a ./′ b

+′(a,b::T) where T <: MultiLike = a .+′ b
-′(a,b::T) where T <: MultiLike = a .-′ b
*′(a,b::T) where T <: MultiLike = a .*′ b
/′(a,b::T) where T <: MultiLike = a ./′ b

+′(a::T,b::R) where {T <: MultiLike,R <: MultiLike} = a .+′ b
-′(a::T,b::R) where {T <: MultiLike,R <: MultiLike} = a .-′ b
*′(a::T,b::R) where {T <: MultiLike,R <: MultiLike} = a .*′ b
/′(a::T,b::R) where {T <: MultiLike,R <: MultiLike} = a ./′ b

+′(a::R,b::T) where {T <: MultiLike,R <: MultiLike} = a .+′ b
-′(a::R,b::T) where {T <: MultiLike,R <: MultiLike} = a .-′ b
*′(a::R,b::T) where {T <: MultiLike,R <: MultiLike} = a .*′ b
/′(a::R,b::T) where {T <: MultiLike,R <: MultiLike} = a ./′ b


+′(a::T,b) where T<:Integer = round(T,a + b)
-′(a::T,b) where T<:Integer = round(T,a - b)
*′(a::T,b) where T<:Integer = round(T,a * b)
/′(a::T,b) where T<:Integer = round(T,a / b)

+′(a,b::T) where T<:Integer = round(T,a + b)
-′(a,b::T) where T<:Integer = round(T,a - b)
*′(a,b::T) where T<:Integer = round(T,a * b)
/′(a,b::T) where T<:Integer = round(T,a / b)

+′(a,b::Perturbator) = a +′ b()
-′(a,b::Perturbator) = a -′ b()
*′(a,b::Perturbator) = a *′ b()
/′(a,b::Perturbator) = a /′ b()

+′(a::Perturbator,b) = a() +′ b
-′(a::Perturbator,b) = a() -′ b
*′(a::Perturbator,b) = a() *′ b
/′(a::Perturbator,b) = a() /′ b

+′(a::T,b::Perturbator) where T <: MultiLike = a .+′ Ref(b)
-′(a::T,b::Perturbator) where T <: MultiLike = a .-′ Ref(b)
*′(a::T,b::Perturbator) where T <: MultiLike = a .*′ Ref(b)
/′(a::T,b::Perturbator) where T <: MultiLike = a ./′ Ref(b)

+′(a::Perturbator,b::T) where T <: MultiLike = Ref(a) .+′ b
-′(a::Perturbator,b::T) where T <: MultiLike = Ref(a) .-′ b
*′(a::Perturbator,b::T) where T <: MultiLike = Ref(a) .*′ b
/′(a::Perturbator,b::T) where T <: MultiLike = Ref(a) ./′ b

+′(a::T,b::Perturbator) where T<:Integer = round(T,a + b())
-′(a::T,b::Perturbator) where T<:Integer = round(T,a - b())
*′(a::T,b::Perturbator) where T<:Integer = round(T,a * b())
/′(a::T,b::Perturbator) where T<:Integer = round(T,a / b())

+′(a::Perturbator,b::T) where T<:Integer = round(T,a() + b)
-′(a::Perturbator,b::T) where T<:Integer = round(T,a() - b)
*′(a::Perturbator,b::T) where T<:Integer = round(T,a() * b)
/′(a::Perturbator,b::T) where T<:Integer = round(T,a() / b)

what i would like is to naturally operate on these object via primed operators, but this type hierarchy has become ridiculous, is there a way to shorten it and make it manageable?

A bit of metaprogramming at least will save you repeat everything for the four operators:

for op in (:+, :-, :*, :/)
    opp = Symbol(op, "′")
    @eval $opp(a, b) = $op(a, b)
    @eval $opp(a::T, b) where T <: MultiLike = $opp.(a, b)
    @eval $opp(a, b::T) where T <: MultiLike = $opp.(a, b)
    @eval $opp(a::T, b) where T <: Integer = round(T, $op(a, b))
    @eval $opp(a, b::T) where T <: Integer = round(T, $op(a, b))
    @eval $opp(a, b::Perturbator) = $opp(a, b())
    @eval $opp(a::Perturbator, b) = $opp(a(), b)
    @eval $opp(a::T, b::Perturbator) where T <: MulitLike = $opp.(a, Ref(b))
    @eval $opp(a::Perturbator, b::T) where T <: MultiLike = $opp.(Ref(a), b)
    @eval $opp(a::T, b::Perturbator) where T <: Integer = round(T, $op(a, b()))
    @eval $opp(a::Perturbator, b::T) where T <: Integer = round(T, $op(a(), b))
end
3 Likes