Why does this constructor not work with 1 Int parameter?

Hi all,

I am new to Julia and am experimenting with trying to create compact code. I created the following types:

module Types

export Percentage

struct Percentage <: Real
    value::Float64
    Percentage(x) = x < 0 ? 0 : x > 1 ? 1 : new(round(x, digits = 6))
end

Base.show(io::IO, x::Percentage) = print(io, "$(x.value * 100)%")

Base.convert(::Type{Percentage}, x::Real) = Percentage(x)
Base.convert(::Type{Percentage}, x::Percentage) = x

Base.promote_rule(::Type{T}, ::Type{Percentage}) where T <: Real = Percentage

import Base: +, -, <, >, <=, >=, ==

+(x::Percentage, y::Percentage) = Percentage(x.value + y.value)
-(x::Percentage) = Percentage(-x.value)
-(x::Percentage, y::Percentage) = Percentage(x.value - y.value)
<(x::Percentage, y::Percentage) = x.value < y.value
<=(x::Percentage, y::Percentage) = x.value <= y.value
>(x::Percentage, y::Percentage) = x.value > y.value
>=(x::Percentage, y::Percentage) = x.value >= y.value
==(x::Percentage, y::Percentage) = x.value == y.value

Base.max(x::Percentage, y::Percentage) = Percentage(max(x.value, y.value))
Base.min(x::Percentage, y::Percentage) = Percentage(min(x.value, y.value))

end

abstract type Longevity end

struct Mechanical <: Longevity
    health::Percentage
    damage_thresholds::Vector{Tuple{Percentage, Float64}}
    repair_thresholds::Vector{Tuple{Percentage, Float64}}
end

Mechanical(
    health::Real = 1;
    damage_thresholds::Vector{Tuple{Percentage,Real}} = Vector(),
    repair_thresholds::Vector{Tuple{Percentage,Real}} = Vector(),
) = Mechanical(Percentage(health), damage_thresholds, repair_thresholds)

I tried to use the Mechanical constructor with an Int
Mechanical(1)
expecting that the int would call the Percentage() constructor and the rest of the parameters would be set to their default values. Instead I get the following error:

ERROR: MethodError: no method matching #Mechanical#10(::Array{Any,1}, ::Array{Any,1}, ::Type{Mechanical}, ::Int64)
Closest candidates are:
  #Mechanical#10(::Array{Tuple{Percentage,Real},1}, ::Array{Tuple{Percentage,Real},1}, ::Type{Mechanical}, ::Real) at /Users/stef/Programming/Julia Projects/Simulation Cockpit/cockpit/Entities.jl:35
Stacktrace:
 [1] Mechanical(::Int64) at /Users/stef/Programming/Julia Projects/Simulation Cockpit/cockpit/Entities.jl:35
 [2] top-level scope at none:1

I’m completely confused with the suggested constructor since the order of the parameters looks completely out of whack. I guess I’m missing something but I don’t know what to be honest.

Thanks in advance,
Stef

Welcome to the community! Hope you’ll like Julia as we all do :slight_smile:

I do not know, whether it helps the original problem, but this is bad

julia> Percentage(-1)
0

julia> Percentage(2)
1

julia> Percentage(0.5)
Percentage(0.5)

The definition of Percentage should be fixed to something like

    Percentage(x) = x < 0 ? new(0) : x > 1 ? new(1) : new(round(x, digits = 6))
1 Like

the error here is due to:

julia> Vector() isa Vector{Tuple{Percentage, Real}}
false

julia> typeof(Vector()) === Vector{Any}
true
5 Likes

Problem can be solved by removing restrictions in Mechanical (there is no need in them anyway)

Mechanical(
           health::Real = 1;
           damage_thresholds = [],
           repair_thresholds = []
) = Mechanical(Percentage(health), damage_thresholds, repair_thresholds)

julia> Mechanical(1)
Mechanical(Percentage(1.0), Tuple{Percentage,Float64}[], Tuple{Percentage,Float64}[])

Rule of thumb: remove arguments restrictions in functions and introduce them only it is really necessary.

3 Likes

Cf clamp.

4 Likes

Thank you!

I’m growing more and more fond of it :slight_smile:

Thanks for pointing out the error.

Kind regards,
Stef

1 Like