# Range checking / bounds (of a variable) checking

Hello,

In my old memories, I recall that Ada (programming language) provide a concept of “range” for checking that a numerical value stored in a variable is always between a lower bound and an upper bound.
This kind of check, is done at run time.
I wonder if this kind of mechanism currently exist in Julia.
For example I would like to say that I store an unsigned integer 8 but that upper bound can’t be upper than 10 and lower bound must be greater than or equal to 1.
I only find information about bound checking for array indices in doc.
Any idea?

Kind regards

3 Likes

Make your own type wrapping the number, implement the methods and provide the checking in an inner constructor.

``````julia> struct RangeNumber{T<:Number, R}
v::T

function RangeNumber(v::Number, r::UnitRange)
v in r || error("\$v not in \$r")
return new{typeof(v), r}(v)
end
end

julia> Base.:+(a::RangeNumber{T, R}, b::RangeNumber{T, R}) where {T, R} = RangeNumber(a.v+b.v, R)

julia> Base.:+(a::Number, b::RangeNumber{T, R}) where {T, R} = RangeNumber(a+b.v, R)

julia> Base.:+(a::RangeNumber{T, R}, b::Number) where {T, R} = RangeNumber(a.v+b, R)

julia> a = RangeNumber(3, 1:7)
RangeNumber{Int64,1:7}(3)

julia> a+a
RangeNumber{Int64,1:7}(6)

julia> a+a+a
ERROR: 9 not in 1:7
Stacktrace:
 error(::String) at ./error.jl:33
 RangeNumber(::Int64, ::UnitRange{Int64}) at ./REPL:5
 + at ./REPL:1 [inlined]
 +(::RangeNumber{Int64,1:7}, ::RangeNumber{Int64,1:7}, ::RangeNumber{Int64,1:7}) at ./operators.jl:502
 top-level scope at none:0

julia> a+3
RangeNumber{Int64,1:7}(6)

julia> a+5
ERROR: 8 not in 1:7
Stacktrace:
 error(::String) at ./error.jl:33
 RangeNumber(::Int64, ::UnitRange{Int64}) at ./REPL:5
 +(::RangeNumber{Int64,1:7}, ::Int64) at ./REPL:1
 top-level scope at none:0
``````

etc

4 Likes

Very interesting althought I would prefer to also be able to call

``````RangeNumber{Int64,1:7}(3)
``````

So I can store the “restriction” (ie `RangeNumber{Int64,1:7}`) and use it later.

``````julia> RangeNumber{T, R}(n) where {T, R} = RangeNumber(convert(T, n), R)

julia> RangeNumber{Int64,1:7}(3)
RangeNumber{Int64,1:7}(3)

julia> RangeNumber{Int64,1:7}(8)
ERROR: 8 not in 1:7
``````
2 Likes

Great! Thanks @kristoffer.carlsson

I’m still a bit stuck up.

I would like to be able to store a restriction (range checking) but also the lack of range checking.

I did

``````abstract type AbstractRangeNumber end

Number(rn::AbstractRangeNumber) = rn.v

struct NoRangeNumber{T<:Number} <: AbstractRangeNumber
v::T
function NoRangeNumber(v::Number)
return new{typeof(v)}(v)
end
end

"""
RangeNumber(v, r)

# Example
a = RangeNumber(3, 1:7)
"""
struct RangeNumber{T<:Number, R} <: AbstractRangeNumber
v::T

function RangeNumber(v::Number, r::UnitRange)
v in r || error("\$v not in \$r")
return new{typeof(v), r}(v)
end
end

RangeNumber{T, R}(n) where {T, R} = RangeNumber(n, R)

Base.:+(a::RangeNumber{T, R}, b::RangeNumber{T, R}) where {T, R} = RangeNumber(a.v+b.v, R)
Base.:+(a::Number, b::RangeNumber{T, R}) where {T, R} = RangeNumber(a+b.v, R)
Base.:+(a::RangeNumber{T, R}, b::Number) where {T, R} = RangeNumber(a.v+b, R)

Base.:(==)(a::AbstractRangeNumber{T, R}, b::AbstractRangeNumber{T, R}) where {T, R} = a.v == b.v
Base.:(==)(a::Number, b::AbstractRangeNumber{T, R}) where {T, R} = a == b.v
Base.:(==)(a::AbstractRangeNumber{T, R}, b::Number) where {T, R} = a.v == b
``````

and tests:

``````using Nextion: RangeNumber, NoRangeNumber
using Test

@testset "RangeNumber" begin
@testset "construct / isequal" begin
a = RangeNumber(3, 1:7)
@test a == 3
end

a = RangeNumber(3, 1:7)
@test a + a == 6
@test_throws ErrorException a + a + a == 9
end

@testset "other construct" begin
a = RangeNumber{Int64, 1:7}(3)
@test a == 3

@test_throws ErrorException a = RangeNumber{Int64, 1:7}(8)
end

@testset "NoRangeNumber" begin
a = NoRangeNumber(3)
@test a == 3
end

end
``````

but it’s failling for odd reason

``````\$ julia test/test_range_number.jl
Stacktrace:
 top-level scope at none:0
 include at ./boot.jl:317 [inlined]