# Norm of Number type I define

**URL:** <https://discourse.julialang.org/t/norm-of-number-type-i-define/75365>\
**Category:** General Usage\
**Tags:** type, linearalgebra\
**Created:** [January 28, 2022, 1:45pm UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365 "2022-01-28T13:45:21Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)\
**Post date:** [January 28, 2022, 1:45pm UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365/1 "2022-01-28T13:45:21Z")

</div>

I have implemented my own `Type` of `Real` (let’s call it `My`)

Julia’s boast holds true: having defined “all” the operations for `My`, Julia is “unreasonably” effective at giving me all sorts of matrix algebra operations at zero additional work on my side.

But then I want to compute the `LinearAlgebra.norm`, and things go sour, although I have implemented all the maths I expect to be needed for an Euclidean norm.

What I do not understand is _what do I need to provide_ to get unreasonnable success.

My attempt below is problematic because

1. I don’t get a norm
2. Error messages have pushed me into implementing conversion to `Float64`. In the real world (as opposed to this MnonWE) this conversion would be lossy, and my norm would be wrong
3. Error messages have pushed me to implement `LinearAlgebra.norm`. Here I have lost the"unreasonnable effectivity" of not having to think what packages `My` is going to compile together with.

Of course I could write my own `norm` function, but then I loose any `LinearAlgebra` functionality that needs a norm…

So here are my attempts.

```julia
struct My <: Real
    x::Float64
end
Base.:(+)(a::My,b::My) = My(a.x+b.x)
Base.:(-)(a::My,b::My) = My(a.x-b.x)
Base.:(*)(a::My,b::My) = My(a.x*b.x)
Base.:(/)(a::My,b::My) = My(a.x/b.x)
Base.:(^)(a::My,b::My) = My(a.x^b.x)
Base.:(^)(a::My,b::Real) = My(a.x^b)
Base.abs(a::My) = My(abs(a.x))
Base.sqrt(a::My) = My(sqrt(a.x))
Base.isnan(a::My) = isnan(a.x)
Base.:(<)(a::My,b::My) = a.x<b.x
Base.AbstractFloat(a::My) = a.x
Base.convert(::Type{Float64},a::My) = a.x

using LinearAlgebra
LinearAlgebra.norm(a::My) = abs(a)
v = My.([1,2,3])
@show norm(v)

```

---

<div class="post-metadata">

**Author:** ![sostock](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sostock/32/5546_2.png) [@sostock](https://discourse.julialang.org/u/sostock)\
**Post date:** [January 28, 2022, 1:58pm UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365/2 "2022-01-28T13:58:17Z")

</div>

You need to define promotion rules. Right now, `+(::My, ::My)` works, but `+(::My, ::AnotherNumberType)` and `+(::AnotherNumberType, ::My)` do not. In your example, `norm` fails because it calls `+(x::Float64, y::My)`, which has a fallback that promotes its arguments to a common type. To handle this, you need to define what common type that should be. This is done by adding `promote_rule` methods. One example could be

```julia
Base.promote_rule(::Type{My}, ::Type{T}) where {T<:Number} = promote_type(Float64, T)

```

That way, the type `My` behaves like a `Float64` when promoted.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 28, 2022, 2:00pm UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365/3 "2022-01-28T14:00:53Z")

</div>

> [@Philippe\_Maincon1](#):
>
> Error messages have pushed me into implementing conversion to `Float64` . In the real world (as opposed to this MnonWE) this conversion would be lossy, and my norm would be wrong

You don’t need to implement _conversion_ to `Float64`, you only need to implement [_promotion_](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/) with `Float64`. e.g.

```julia
Base.promote_rule(::Type{My}, ::Type{Float64}) = My

```

(The reason for this is that `norm` needs to promote any type to a floating-point type for accumulation, and for accuracy/overflow reasons asks you to promote to at least double precision.)

You’ll also want:

```julia
Base.float(x::My) = x

```

(`My` is already a floating-point type … more generally, if you had `My{T}` then you want to convert to `My{float(T)})`.

---

<div class="post-metadata">

**Author:** ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)\
**Post date:** [January 28, 2022, 2:08pm UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365/4 "2022-01-28T14:08:28Z")

</div>

Yes, I have a MWE now, thanks to your advice. The norm is of type `My`, so that resolves my concern about a lossy conversion. I had to implement `Base.isnan`, `Base.inf` and as you said `Base.float`, and no need for overloading `LinearAlgebra.norm` (good!!!)

I had to make another change though:

```julia
struct My <: Number

```

instead of

```julia
struct My <: Real

```

Now I wonder how my _real_ code is going to like that! 🙄 - let’s find out! … YES 😁

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 28, 2022, 2:11pm UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365/5 "2022-01-28T14:11:39Z")

</div>

> [@sostock](#):
>
> `Base.promote_rule(::Type{My}, ::Type{T}) where {T<:Number} = promote_type(Float64, T)`

In practice you’d probably want a `My` type out of promotion. e.g. suppose `My` is a quaternion type.

More generally, you probably want something like `My` to be a parameterized type:

```julia
struct My{T<:Real} <: Real
    x::T
end

Base.:(+)(a::My,b::My) = My(a.x+b.x)
Base.:(-)(a::My,b::My) = My(a.x-b.x)
Base.:(*)(a::My,b::My) = My(a.x*b.x)
Base.:(/)(a::My,b::My) = My(a.x/b.x)
Base.:(^)(a::My,b::My) = My(a.x^b.x)
Base.:(^)(a::My,b::Real) = My(a.x^b)
Base.abs(a::My) = My(abs(a.x))
Base.sqrt(a::My) = My(sqrt(a.x))
Base.isnan(a::My) = isnan(a.x)
Base.isinf(a::My) = isinf(a.x)
Base.isfinite(a::My) = isfinite(a.x)
Base.:(<)(a::My,b::My) = a.x<b.x

Base.promote_rule(::Type{My{T}}, ::Type{S}) where {T,S<:Real} = My{promote_type(Float64, T)}
Base.float(x::My) = My(float(x.x))
Base.convert(::Type{My{T}}, x::Real) where {T} = My(convert(T, x))
Base.convert(::Type{My{T}}, x::My) where {T} = My(convert(T, x.x))

```

and then you get

```julia
julia> using LinearAlgebra

julia> norm(My.([1,2,3]))
My{Float64}(3.7416573867739413)

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 28, 2022, 2:23pm UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365/6 "2022-01-28T14:23:18Z")

</div>

> [@stevengj](#):
>
> ```julia
> Base.abs(a::My) = My(abs(a.x))
> Base.sqrt(a::My) = My(sqrt(a.x))
> Base.isnan(a::My) = isnan(a.x)
> Base.isinf(a::My) = isinf(a.x)
> Base.isfinite(a::My) = isfinite(a.x)
> 
> ```

Note also that it’s easier to write this sort of code as:

```julia
for f in (:abs, :sqrt)
    @eval Base.$f(a::My) = My($f(a))
end
for f in (:isnan, :isinf, :isfinite)
    @eval Base.$f(a::My) = $f(a)
end

```

---

<div class="post-metadata">

**Author:** ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)\
**Post date:** [January 28, 2022, 2:27pm UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365/7 "2022-01-28T14:27:57Z")

</div>

Yes!

My real code now works, thanks to you, and it has all the features you describe: parametric type, eval and convert.

But interestingly, you guys seem to get it to work with `My <: Real` while I had to move to `My<:Number` (well, if it works…)

---

<div class="post-metadata">

**Author:** ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)\
**Post date:** [January 31, 2022, 8:50am UTC](https://discourse.julialang.org/t/norm-of-number-type-i-define/75365/8 "2022-01-31T08:50:17Z")

</div>

To get everything to work, inclusive `LinearAlgebra.vecnormalise` I ended up overloading `Base.isinf, isginite, typemax, typemin, floatmax, floatmin, eps` and `float`.
