# Concise way to create numeric type?

**URL:** https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900
**Category:** General Usage
**Tags:** question
**Created:** [July 17, 2017, 11:52am UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900 "2017-07-17T11:52:36Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)
#### Post date: [July 17, 2017, 11:52am UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900/1 "2017-07-17T11:52:36Z")

</div>

I am creating a numeric type that contains a single number field, for example:

```julia
struct T
v::Float64
T(v) = 0 <= v <= 1 ? new(v) : error("invalid v")
end

```

In case you are wondering, I am doing this to ensure that all variables of type `T` have a value between 0 and 1 (or some other invariant I might want to enforce).

Now I want to define all numeric operations on `T`, simply by calling the operation on the field `v` and returning a number, like:

```julia
Base.:(+)(x::T, y::T) = x.p + y.p

```

Is there a concise way of doing this for all numeric operations? I would also like to cover numeric functions. Maybe a macro?

I also tried

```julia
Base.convert(::Type{F}, p::T) where {F <: Number} = convert(F, p.p)

```

in the hope that my type would get promoted automatically to a number and then the numeric operations would work, but this does not work either.

---

<div class="post-metadata">

### Author: ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)
#### Post date: [July 17, 2017, 12:22pm UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900/2 "2017-07-17T12:22:22Z")

</div>

Oh I figured it out.

```julia
struct T <: Number
    v::Float64;
    T(x) = 0 ≤ x ≤ 1 ? new(x) : error("invalid v")
end

Base.convert(::Type{N}, x::T) where {N <: Number} = convert(N, x.v)
Base.promote_rule(::Type{N}, ::Type{T}) where {N <: Number} = promote_type(N, Float64)

```

---

<div class="post-metadata">

### Author: ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)
#### Post date: [July 17, 2017, 12:32pm UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900/3 "2017-07-17T12:32:45Z")

</div>

But now I am getting some strange errors. Simply executing:

```julia
T(0.2)

```

results in this big error:

> T(Error showing value of type T:  
> ERROR: MethodError: Cannot `convert` an object of type Float64 to an object of type T  
> This may have arisen from a call to the constructor T(…),  
> since type constructors fall back to convert methods.  
> Stacktrace:  
> [1] Pair(::Symbol, ::T) at ./pair.jl:4  
> [2] show\_default(::IOContext{Base.Terminals.TTYTerminal}, ::Any) at ./show.jl:134  
> [3] display(::Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, ::MIME{Symbol(“text/plain”)}, ::T) at ./REPL.jl:122  
> [4] display(::Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, ::T) at ./REPL.jl:125  
> [5] display(::T) at ./multimedia.jl:194  
> [6] eval(::Module, ::Any) at ./boot.jl:235  
> [7] print\_response(::Base.Terminals.TTYTerminal, ::Any, ::Void, ::Bool, ::Bool, ::Void) at ./REPL.jl:144  
> [8] print\_response(::Base.REPL.LineEditREPL, ::Any, ::Void, ::Bool, ::Bool) at ./REPL.jl:129  
> [9] (::Base.REPL.#do\_respond#16{Bool,Base.REPL.##26#36{Base.REPL.LineEditREPL,Base.REPL.REPLHistoryProvider},Base.REPL.LineEditREPL,Base.LineEdit.Prompt})(::Base.LineEdit.MIState, ::Base.AbstractIOBuffer{Array{UInt8,1}}, ::Bool) at ./REPL.jl:646

What is going on here??

And even more surprisingly, `T(0.2) + 0.2` correctly returns `0.4` without any problems.

---

<div class="post-metadata">

### Author: ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)
#### Post date: [July 17, 2017, 12:43pm UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900/4 "2017-07-17T12:43:37Z")

</div>

You should define

```julia
Base.convert(::Type{T}, x::T) = x

```

or convert will get into an infinite recursion when you try to convert a `T` into a `T`. (I’m guessing `show` tries to do this.)

---

<div class="post-metadata">

### Author: ![John\_Gibson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/john_gibson/32/5321_2.png) [@John\_Gibson](https://discourse.julialang.org/u/John_Gibson)
#### Post date: [July 17, 2017, 1:25pm UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900/6 "2017-07-17T13:25:39Z")

</div>

Here’s a nice definition of + - \* / for a Galois field using a loop over those operators and macros, from one of Andreas Noack’s talks:

```julia
import Base: +, -, *, /

for op in (:+, :-, :*)
    @eval begin
        ($op){P,T}(x::GF{P,T}, y::GF{P,T}) = GF{P,T}($(op)(x.data, y.data))
    end
end

```

Complete working example

```julia
# Scalar finite fields. P is the modulus, T is the integer type (Int16, Int32, ...)
immutable GF{P,T<:Integer} <: Number
    data::T
    function GF(x::Integer)
        return new(mod(x, P))
    end
end

# basic methods for scalar finite field
import Base: convert, inv, one, promote_rule, show, zero

function call{P}(::Type{GF{P}}, x::Integer)
    if !isprime(P)
        throw(ArgumentError("P must be a prime"))
    end
    return GF{P,typeof(x)}(mod(x, P))
end
convert{P,T}(::Type{GF{P,T}}, x::Integer) = GF{P}(x)
convert{P}(::Type{GF{P}}, x::Integer) = GF{P}(x)
convert{P,T}(::Type{GF{P,T}}, x::GF{P}) = GF{P,T}(x.data)
promote_rule{P,T1,T2<:Integer}(::Type{GF{P,T1}}, ::Type{T2}) = GF{P,promote_type(T1,T2)}
show(io::IO, x::GF) = show(io, x.data)

# define arithmetic operations
import Base: +, -, *, /

for op in (:+, :-, :*)
    @eval begin
        ($op){P,T}(x::GF{P,T}, y::GF{P,T}) = GF{P,T}($(op)(x.data, y.data))
    end
end

# Division requires slightly more care
function inv{P,T}(x::GF{P,T})
    if x == zero(x)
        throw(DivideError())
    end
    r, u, v = gcdx(x.data, P)
    GF{P,T}(u)
end
(/){P}(x::GF{P}, y::GF{P}) = x*inv(y)

x, y = GF{5}(9), GF{5}(8)
@show x
@show y
@show x + y
@show x - y
@show x * y
;
x = 4
y = 3
x + y = 2
x - y = 1
x * y = 2

```

---

<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: [July 17, 2017, 1:48pm UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900/7 "2017-07-17T13:48:28Z")

</div>

> [@Per](#):
>
> You should define `Base.convert(::Type{T}, x::T) = x` or `convert` will get into an infinite recursion when you try to convert a `T` into a `T`. (I’m guessing show tries to do this.)

This is incorrect. Base [already includes this definition](https://github.com/JuliaLang/julia/blob/5535ecbb6d8a24d2f74f1d1fc9db8de672d8f4a5/base/essentials.jl#L65).

---

<div class="post-metadata">

### Author: ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)
#### Post date: [July 17, 2017, 1:55pm UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900/8 "2017-07-17T13:55:38Z")

</div>

@stevengj It did fix the errors… somehow it worked.

---

<div class="post-metadata">

### Author: ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)
#### Post date: [July 17, 2017, 1:58pm UTC](https://discourse.julialang.org/t/concise-way-to-create-numeric-type/4900/9 "2017-07-17T13:58:53Z")

</div>

> [@stevengj](#):
>
> Base already includes this definition

Yes, but the method that specializes on a concrete type has precedence.

```julia
julia> struct T <: Number
           v::Float64;
           T(x) = 0 ≤ x ≤ 1 ? new(x) : error("invalid v")
       end

julia> Base.convert(::Type{N}, x::T) where {N <: Number} = convert(N, x.v)

julia> @which convert(T, T(0.2))
convert(::Type{N}, x::T) where N<:Number in Main at REPL[2]:1

```
