# Understanding how to implement numbers in Julia

**URL:** <https://discourse.julialang.org/t/understanding-how-to-implement-numbers-in-julia/51643>\
**Category:** General Usage\
**Created:** [December 11, 2020, 12:57am UTC](https://discourse.julialang.org/t/understanding-how-to-implement-numbers-in-julia/51643 "2020-12-11T00:57:17Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![minetest2048](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/minetest2048/32/45961_2.png) [@minetest2048](https://discourse.julialang.org/u/minetest2048)\
**Post date:** [December 11, 2020, 12:57am UTC](https://discourse.julialang.org/t/understanding-how-to-implement-numbers-in-julia/51643/1 "2020-12-11T00:57:17Z")

</div>

To understand how to implement a number in Julia, I tried to implement my own complex number type:

```julia
struct MyComplex{T<:Real} <: Number
    real::T
    imag::T
end

MyComplex(re::Real) = MyComplex(re,zero(re))
Base.conj(z::MyComplex) = MyComplex(z.real,-z.imag)
real(z::MyComplex) = z.real
imag(z::MyComplex) = z.imag

```

then I define basic arithmetic

```julia
function Base.:+(a::MyComplex,b::MyComplex)
    MyComplex(a.real + b.real, a.imag + b.imag)
end

function Base.:-(a::MyComplex,b::MyComplex)
    MyComplex(a.real - b.real, a.imag - b.imag)    
end

function Base.:*(a::MyComplex,b::MyComplex)
    MyComplex(a.real * b.real - a.imag * b.imag, a.real * b.imag + b.real * b.imag)
end

function Base.:/(a::MyComplex,b::Real)
    return MyComplex(a.real/b,a.imag/b)
end 

function Base.:/(a::MyComplex,b::MyComplex)
    num = a * conj(b)
    den = real(b * conj(b))
    return num/den
end 

```

What do I do from here so that my new MyComplex number type just works with math functions such as sin cos exp sqrt? I thought if I implement the basic arithmetic it will just work with all of the built in math functions.

Then, some of the operations fails:

```julia
julia> 2/MyComplex(0,2)
ERROR: promotion of types Int64 and MyComplex{Int64} failed to change any arguments
Stacktrace:
 [1] error(::String, ::String, ::String) at ./error.jl:42
 [2] sametype_error(::Tuple{Int64,MyComplex{Int64}}) at ./promotion.jl:306
 [3] not_sametype(::Tuple{Int64,MyComplex{Int64}}, ::Tuple{Int64,MyComplex{Int64}}) at ./promotion.jl:300
 [4] promote at ./promotion.jl:283 [inlined]
 [5] /(::Int64, ::MyComplex{Int64}) at ./promotion.jl:314
 [6] top-level scope at REPL[73]:1

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [December 11, 2020, 1:13am UTC](https://discourse.julialang.org/t/understanding-how-to-implement-numbers-in-julia/51643/2 "2020-12-11T01:13:11Z")

</div>

as the error suggests, you need to implement the promotion rules:  
[https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#Promotion](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#Promotion)

because you sub-typed `Number`, as soon as you have promotion rules in place, these will start working. (because + - \* / has fallback methods which perform promotions first)

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [December 11, 2020, 8:13am UTC](https://discourse.julialang.org/t/understanding-how-to-implement-numbers-in-julia/51643/3 "2020-12-11T08:13:18Z")

</div>

I don’t think the interface for numbers is formally and extensively documented (yet?), but there are a couple of threads here on discourse where this topic was discussed. You might find valuable information there:

> [@Interface for \`Number\`](https://discourse.julialang.org/t/interface-for-number/2723):
>
> I recently ran into an issue overriding zero for my own Number: julia\> immutable Infinity \<: Number end julia\> Base.zero(::Infinity) = 0 julia\> import Base: + julia\> +(::Infinity,::Int) = Infinity() + (generic function with 181 methods) julia\> reduce(+,[Infinity(),Infinity()]) ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Infinity This may have arisen from a call to the constructor Infinity(...), since type constructors fall back to convert methods. Stack…

> [@Codify best practices for custom types](https://discourse.julialang.org/t/codify-best-practices-for-custom-types/42176):
>
> The announcement for [CSV.jl v0.7](https://discourse.julialang.org/t/ann-csv-jl-0-7-release/42162) includes this new capability: Custom types can now be passed in the … types keyword …, fast parsing is supported for all Integer and AbstractFloat types; other custom types need to support zero(T) and parse(T, str) to be parsed correctly. That a custom numeric type T support zero(T) is entirely reasonable and of great utility to clients and other users of that type. With T a natural (whole) number type, zero(T) would throw a DomainError; and imo that…

From what I see and off the top of my head, I’d say that apart from promotion rules, you’ll still be missing:

- a unary minus operator
- comparison operators
- probably a `zero` method

---

<div class="post-metadata">

**Author:** ![minetest2048](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/minetest2048/32/45961_2.png) [@minetest2048](https://discourse.julialang.org/u/minetest2048)\
**Post date:** [December 11, 2020, 10:49am UTC](https://discourse.julialang.org/t/understanding-how-to-implement-numbers-in-julia/51643/4 "2020-12-11T10:49:23Z")

</div>

How do I implement promotion rules? I added this in my script

```julia
struct MyComplex{T<:Real} <: Number
    real::T
    imag::T
end

MyComplex(re::Real) = MyComplex(re,zero(re))
Base.conj(z::MyComplex) = MyComplex(z.real,-z.imag)
real(z::MyComplex) = z.real
imag(z::MyComplex) = z.imag

promote_rule(::Type{MyComplex{T}}, ::Type{S}) where {T<:Real,S<:Real} = MyComplex{promote_type(T,S)}
promote_rule(::Type{MyComplex{T}}, ::Type{MyComplex{S}}) where {T<:Real,S<:Real} = MyComplex{promote_type(T,S)}

```

following the example given for `Rational` type: [Conversion and Promotion · The Julia Language](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#Case-Study:-Rational-Promotions) and when I try to run `promote(MyComplex(2,0),2)` it errors out

```julia
julia> promote(MyComplex(2,0),2)
ERROR: promotion of types MyComplex{Int64} and Int64 failed to change any arguments
Stacktrace:
 [1] error(::String, ::String, ::String) at ./error.jl:42
 [2] sametype_error(::Tuple{MyComplex{Int64},Int64}) at ./promotion.jl:306
 [3] not_sametype(::Tuple{MyComplex{Int64},Int64}, ::Tuple{MyComplex{Int64},Int64}) at ./promotion.jl:300
 [4] promote(::MyComplex{Int64}, ::Int64) at ./promotion.jl:283
 [5] top-level scope at REPL[14]:1

```

EDIT: if I use `Base.promote_rule` instead of just `promote_rule` it works (ish, I need to implement some more functions), how do I know when to use `Base.` instead of the function directly?

---

<div class="post-metadata">

**Author:** ![ericphanson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ericphanson/32/215186_2.png) [@ericphanson](https://discourse.julialang.org/u/ericphanson)\
**Post date:** [December 11, 2020, 11:55am UTC](https://discourse.julialang.org/t/understanding-how-to-implement-numbers-in-julia/51643/5 "2020-12-11T11:55:38Z")

</div>

> [@minetest2048](#):
>
> if I use `Base.promote_rule` instead of just `promote_rule` it works (ish, I need to implement some more functions), how do I know when to use `Base.` instead of the function directly?

whenever you want to add a new method for an existing function from another module, you need to either `import` it, or qualify it. So either

```julia
import Base.promote_rule
promote_rule(..) = ...

```

or `Base.promote_rule(...)=...` as you did. (Same for any other module, e.g. if you want to add a method to a function from some package `PkgX`, then you’d do the same).

Often the `Base.promote_rule(...)=...` way is preferred (e.g. it’s recommended in [YASGuide](https://github.com/jrevels/YASGuide#programming-guidelines) which is the style guide we use at my work) because it makes it more obvious that you’re extending a function from another module (since the `import` statements may be in some other file so you might forget that you’ve `import`ed the function).

If you don’t do this, you’re defining a _new_ function instead of adding a method to an existing function.
