# How can I extend a primitive type

**URL:** <https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135>\
**Category:** General Usage\
**Tags:** question\
**Created:** [March 15, 2023, 2:04pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135 "2023-03-15T14:04:01Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![this\_josh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/this_josh/32/42679_2.png) [@this\_josh](https://discourse.julialang.org/u/this_josh)\
**Post date:** [March 15, 2023, 2:04pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/1 "2023-03-15T14:04:01Z")

</div>

I’d like to implement a `Percent` type, which will be a subtype of `Float64` and can utilise `Float64`. Ideally I’d like to something like this:

```julia
struct Percent <: Float64 
    v::Float64
    @assert 0≤v≤1 # I'm not sure if this is possible without an extra package?
end

Base.show(io::IO, p::Percent) = print(io, p*100, "%")

@test Percent(0.3)*10 == 3

```

I’ve read through the [types docs](https://docs.julialang.org/en/v1/manual/types/) but can only get it to work like `Percent(0.3).v*10` which means I’m just using the Float anyway.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [March 15, 2023, 2:23pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/2 "2023-03-15T14:23:03Z")

</div>

Concrete types in Julia can’t have subtypes. You probably just want to subtype `Real`.

---

<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:** [March 15, 2023, 2:48pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/3 "2023-03-15T14:48:52Z")

</div>

> [@this\_josh](#):
>
> `@assert 0≤v≤1`

This is not the sort of constraint you can easily impose in the type domain — it has to be checked at runtime — so it might not be worthwhile to implement this as a type.

For a runtime constraint like this, I would normally just define an `ispercent(x::Real) = 0 ≤ x ≤ 1` function and check it as needed, maybe also defining a `checkpercent(x) = ispercent(x) || throw(DomainError("$x is not in [0,1]"))` function to throw errors. (You wouldn’t use `@assert` because assertions can be turned off — they are to check for bugs, not for users passing invalid data.)

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [March 15, 2023, 2:50pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/4 "2023-03-15T14:50:35Z")

</div>

Oscar directly answered your question. However, consider if that’s what you really want.

The way I understand it is that `Percent <: Real` is a real number restricted to the range `[0, 1]`. But then the following are valid operations:

```julia
Percent(0.8) + Percent(0.4) # 1.2
Percent(0.2) + 1000 # 1000.2

```

Not only their result is not a `Percent` anymore (which would be fine), they might be simply wrong if by “percent” you mean a proportion of something, e.g.,

```julia
probabilities_A = [Percent(3 / 4), Percent(1 / 4)]
probabilities_B = [Percent(1 / 2), Percent(1 / 2)]
probabilities_A[1] + probabilities_B[1] # You probably never want this

```

Therefore, if you want to use `Percent` as a proportion of something, it might be a good idea to keep it simple:

```julia
struct Percent
    v::Float64
    function Percent(v)
        # validate 0 <= v <= 1
        new(v)
    end
end

percent_of(p::Percent, n::Number) = p.v * n
# or maybe
import Base.*
(*)(p::Percent, n::Number) = p.v * n

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [March 15, 2023, 3:00pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/5 "2023-03-15T15:00:32Z")

</div>

Why can’t a percentage be outside the interval `[0, 1]`? What happens if you get a 150% pay raise? Do you say “No thank you, that’s not a percentage I’m comfortable with.”

---

<div class="post-metadata">

**Author:** ![this\_josh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/this_josh/32/42679_2.png) [@this\_josh](https://discourse.julialang.org/u/this_josh)\
**Post date:** [March 15, 2023, 3:02pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/6 "2023-03-15T15:02:47Z")

</div>

Thanks for all your answers.

> [@Oscar\_Smith](#):
>
> Concrete types in Julia can’t have subtypes. You probably just want to subtype `Real`.

How would I subtype it? Given I’d like `Percent(0.3)*2` to work? The docs suggest [“Abstract types cannot be instantiated”](https://docs.julialang.org/en/v1/manual/types/#man-abstract-types) but Real can be with `Real(0.3)`. Clearly I’m miss-understanding something here.

> [@stevengj](#):
>
> `checkpercent(x) = ispercent(x) || throw(DomainError("$x is not in [0,1]"))`

Okay, so the constructor wouldn’t be doing the check? You’d have:

```julia
my_percent = 0.3
checkpercent(my_percent) || foo_requires_percent(my_percent)

```

Ideally rather than having `foo_requires_percent` I’d prefer `foo(bar::Percent)`

> [@stevengj](#):
>
> assertions can be turned off

Sure.

> [@barucden](#):
>
> `Percent` as a proportion of something

Good point. I’m interested in how I’d do this from both a proportion point of view, or not (`Percent(10)=1000%` being valid)

> [@barucden](#):
>
> ```julia
> import Base.*
> (*)(p::Percent, n::Number) = p.v * n
> 
> ```

Ideally I’d prefer to inherit these methods from the parent type given that `Percent <: Real`

---

<div class="post-metadata">

**Author:** ![nilshg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nilshg/32/2283_2.png) [@nilshg](https://discourse.julialang.org/u/nilshg)\
**Post date:** [March 15, 2023, 3:03pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/7 "2023-03-15T15:03:36Z")

</div>

> [@this\_josh](#):
>
> Real can be with `Real(0.3)`

No:

```julia
julia> typeof(Real(0.3))
Float64

```

---

<div class="post-metadata">

**Author:** ![this\_josh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/this_josh/32/42679_2.png) [@this\_josh](https://discourse.julialang.org/u/this_josh)\
**Post date:** [March 15, 2023, 3:08pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/8 "2023-03-15T15:08:04Z")

</div>

Ah, so this was my miss-understanding, effectively there’s a function `Real(x::Float64)=Float64(x)`? Although I presume this isn’t the actual implementation.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [March 15, 2023, 3:10pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/9 "2023-03-15T15:10:51Z")

</div>

> [@this\_josh](#):
>
> effectively there’s a function `Real(x::Float64)=Float64(x)`? Although I presume this isn’t the actual implementation.

Yes, the actual implementation is

```julia
(::Type{T})(x::T) where {T<:Number} = x

```

A bit hard to read, but it means that for any type `T`, if the type of `x` is a subtype of `T`, just return `x` itself.

---

<div class="post-metadata">

**Author:** ![this\_josh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/this_josh/32/42679_2.png) [@this\_josh](https://discourse.julialang.org/u/this_josh)\
**Post date:** [March 15, 2023, 3:11pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/10 "2023-03-15T15:11:08Z")

</div>

I don’t belive this comment is very constructive, it is clearly an application specific consideration. In my scenario I’m interested in considering any percentage and

> [@barucden](#):
>
> as a proportion of something

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [March 15, 2023, 3:13pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/11 "2023-03-15T15:13:40Z")

</div>

> [@this\_josh](#):
>
> I don’t belive this comment is very constructive

Well, excuse me. I was just pointing out that percentages are not restricted to be in any particular range, and it was not clear to me why it should be. And I was trying to be funny.

OK, I’ll stop bothering you.

---

<div class="post-metadata">

**Author:** ![tbeason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbeason/32/15898_2.png) [@tbeason](https://discourse.julialang.org/u/tbeason)\
**Post date:** [March 15, 2023, 3:19pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/12 "2023-03-15T15:19:22Z")

</div>

You haven’t really told us what you are doing in a more complete sense, but I have a hard time believing that making a special number type is really going to be worth your trouble. The only thing that differentiates your idea of a `Percent` from a `Float64` is the domain restriction. Just throw that `@assert` line everywhere and move on!

---

<div class="post-metadata">

**Author:** ![this\_josh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/this_josh/32/42679_2.png) [@this\_josh](https://discourse.julialang.org/u/this_josh)\
**Post date:** [March 15, 2023, 3:30pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/13 "2023-03-15T15:30:38Z")

</div>

I appreciate your point if it is difficult. I presumed it would be trivial, in Python it is rather trivial:

```python
class Percent(float):
    def __init__ (self, v) -> None:
        if v > 100:
            raise ValueError("percent must be <= 100")
        if v < 0:
            raise ValueError("percent must be >= 0")
        self.value = v

```

```python
>>> Percent(20)*2
40.0

```

_Note:_ As already outlined the checks would be option.

Depending on where things go this would make it easier for me to extend it. For example I could make a currency type `USD <: Real`, now I can have `to_GBP(amount::USD)` and `to_GBP(amount::EUR)`. For me these types make it possible to write cleaner function signatures and leverage multiple dispatch further. But perhaps I’m missing a point.

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [March 15, 2023, 3:36pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/14 "2023-03-15T15:36:45Z")

</div>

> [@this\_josh](#):
>
> Ideally I’d prefer to inherit these methods from the parent type given that `Percent <: Real`

My point is that you perhaps should not consider `Percent` to be a special `Real` number, because it makes no sense to compute, e.g., `3% + 3.5`.

Percents are usually used to express relative amounts with respect to some base quantity. In that case, it also makes no sense to compute, e.g., `100*(5/20)% + 100*(3/12)%` because the base quantities (denominators) are different. Yet, `100*5/20 + 100*3/12` is a perfectly okay expression for real numbers.

So I suggest not to subtype `Percent <: Real` if by “percent” you mean a ratio.

If by “percent” you just mean “a real number that is between 0 and 1”, then it is probably not worth it to create a new type for that (as @tbeason said). You could just do

```julia
function percent(v) 
    0 ≤ v ≤ 1 || throw(DomainError("$v is outside [0, 1]"))
    return v
end

```

Lastly, if you really really wanted to create a custom numeric type, you would have to define [promotion rules and conversion methods](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#Conversion).

> [@this\_josh](#):
>
> Depending on where things go this would make it easier for me to extend it. For example I could make a currency type `USD <: Real`, now I can have `to_GBP(amount::USD)` and `to_GBP(amount::EUR)`. For me these types make it possible to write cleaner function signatures and leverage multiple dispatch further. But perhaps I’m missing a point.

Check [GitHub - PainterQubits/Unitful.jl: Physical quantities with arbitrary units](https://github.com/PainterQubits/Unitful.jl).

---

<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:** [March 15, 2023, 3:56pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/15 "2023-03-15T15:56:59Z")

</div>

> [@this\_josh](#):
>
> Okay, so the constructor wouldn’t be doing the check? You’d have:
> 
> ```julia
> my_percent = 0.3
> checkpercent(my_percent) || foo_requires_percent(my_percent)
> 
> ```

No, you’d have

```julia
function foo_requires_percent(p::Real)
    checkpercent(p) # throws DomainError if p ∉ [0,1]
    ... do stuff ...
end

```

i.e. you’d add an argument check to every function that requires its argument to be in `[0,1]`, but allow the caller to pass an instance of any `Real` type.

Your proposed alternative of `foo_requires_percent(p::Percent)` just forces the user to call it as `foo_requires_percent(Percent(0.1))`, which still does a runtime check, potentially requires code at _every call site_ instead of _once_ in the function, is more annoying to use, and requires a bunch of boilerplate if you want `Percent` to otherwise act exactly like `Float64`.

---

<div class="post-metadata">

**Author:** ![this\_josh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/this_josh/32/42679_2.png) [@this\_josh](https://discourse.julialang.org/u/this_josh)\
**Post date:** [March 15, 2023, 4:06pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/16 "2023-03-15T16:06:58Z")

</div>

Thank you, these are good points you raise, and I agree percent is perhaps best to not subtype `Real`.

I’ve been looking through Unitful, the docs show how to [dispatch on dimensions](https://painterqubits.github.io/Unitful.jl/stable/highlights/#Dispatch-on-dimensions) but not units? Otherwise Unitful seems like a good option for that scenario. I’ll keep looking into it.

```julia
julia> @unit USD "USD" USD 1 false
USD

julia> foo(x::USD) = x
ERROR: ArgumentError: invalid type for argument x in method definition for foo at REPL[42]:1
Stacktrace:
...

```

---

<div class="post-metadata">

**Author:** ![this\_josh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/this_josh/32/42679_2.png) [@this\_josh](https://discourse.julialang.org/u/this_josh)\
**Post date:** [March 15, 2023, 4:19pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/17 "2023-03-15T16:19:45Z")

</div>

I see, this would require diligent usage of `checkpercent`. It is effectively extracting type checking away from the type, which as you suggest could reduce the number of calls. I appreciate this is a solution though.

My proposal was to have

```julia
foo(bar::Percent)=...
foo(bar::USD)=...
foo(bar::Float64)=...

```

Leveraging Julia’s multiple dispatch capabilities and gaining the benefits this provides.

Perhaps what I’m after isn’t idiomatic Julia.

---

<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:** [March 15, 2023, 4:33pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/18 "2023-03-15T16:33:30Z")

</div>

> [@this\_josh](#):
>
> I see, this would require diligent usage of `checkpercent`.

No more diligent than putting `::Percent` in every method where you want to impose this constraint on an argument.

In contrast, units (ala Unitful.jl) do not simply impose constraints on values — they track how units _change_ as you pass them through operations like `x^2` or `x/y`, detect compatibility of different combinations, and moreover they can do this _statically_ (at compile time, so expressing information in the type domain is a _big win_).

> [@this\_josh](#):
>
> My proposal was to have
> 
> ```julia
> foo(bar::Percent)=...
> foo(bar::USD)=...
> foo(bar::Float64)=..
> 
> ```

It’s hard to judge what you want based on “foo”, but I suspect that this is too much punning — if `foo` is doing something completely different for a percentage than for a `Float64`, why should the two functions have the same name? And if it is doing the same thing, why can’t you write a single `foo(bar::Real)`?

(You don’t generally use _type_ dispatch to differentiate behavior based on _value_ information.)

---

<div class="post-metadata">

**Author:** ![this\_josh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/this_josh/32/42679_2.png) [@this\_josh](https://discourse.julialang.org/u/this_josh)\
**Post date:** [March 15, 2023, 4:45pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/19 "2023-03-15T16:45:02Z")

</div>

> [@stevengj](#):
>
> `::Percent` in every method where you want to impose this constraint on an argument.

Perhaps we are splitting hairs. I type the majority of functions I write so for me it is already engrained.

> [@stevengj](#):
>
> what you want based on “foo”,

Sure, perhaps I’m being too abstract. Here is a more concrete example which I’d use multiple dispatch for:

```julia
function new_salary(current_salary::Real, change::Percent=Percent(150)
    return current_salary * (1 + change)
end

function new_salary(current_salary::Real, change::Real)
    return current_salary + change
end

```

@DNF 🙂

I think they are similar enough that multiple dispatch isn’t too confusing.

---

<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:** [March 15, 2023, 4:52pm UTC](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135/20 "2023-03-15T16:52:44Z")

</div>

> [@this\_josh](#):
>
> I think they are similar enough that multiple dispatch isn’t too confusing.

On the contrary, I think this is a perfect case for spelling them differently. It seems a heck of a lot clearer to spell the two functions as `current_salary + change` and `current_salary * (1 + change)`. 😉

(This is the problem with trying to argue about type design in the abstract or from unrealistic toy examples.)

[Next page](https://discourse.julialang.org/t/how-can-i-extend-a-primitive-type/96135.md?page=2)
