How to use as a default only one property from a struct?

291 Views Asked by At

I want to create a struct with many properties, but when applying the functions, I want it to behave like its type is one of the selected properties, for example:

mutable struct Field{T}
    field_type::T
    field_description::String
end

I want this struct to behave as the T type, if I assign it a String, when doing print(Field{String}("Hello", "World")) I want it to print Hello, if field type is a Int8 I want to be able to do Field{Int8}(1, "First term") + Field{Int8}(1,"Second term") and get 2 and so on. Is this possible somehow?

For more context, I want this to add some metadata to the Field Type, but it has to behave as the chosen field_type

2

There are 2 best solutions below

1
On

You are over-complicating things. You just need to realize that Field{String} and Field{Int8} are two separate types, and treat them as such.

For example:

foo(f::Field{String}) = do_something()
foo(f::Field{Int8}) = do_something_else()

So for printing and adding, define:

Base.show(io::IO, f::Field{String}) = show(io, f.field_type)
Base.:+(f1::Field{Int8}, f2::Field{Int8}) = f1.field_type + f2.field_type

You could make it more generic by defining

Base.show(io::IO, f::Field{<:AbstractString}) = show(io, f.field_type)
Base.:+(f1::Field{<:Number}, f2::Field{<:Number}) = f1.field_type + f2.field_type

instead.

I defined show instead of print, since that is more convenient, but you can define any methods for any Field{T} that you like. Just remember that different T gives different types, and it's easy.

I would recomment that you create another convenience constructor:

Field(x::T, d) where {T} = Field{T}(x, d)

Now you don't need to specify T in the constructor call:

1.7.2> f1 = Field("Hello", "World"); typeof(f1)
Field{String}

1.7.2> f2 = Field(Int8(1), "World"); typeof(f2)
Field{Int8}

1.7.2> print(f1)
"Hello"
1.7.2> f2 + f2
2
0
On

I would consider usingBase.@kwdef or a better equivalent in the Parameters package.

Base.@kwdef struct Field{T}                          
    field::T                                             
    description::Union{String,Nothing} = nothing          
end                                                  

And now:

julia> Field(field=1)
Field{Int64}(1, nothing)

or:

using Parameters
@with_kw struct Field2{T}
    field::T
    description::Union{String,Nothing} = nothing
end

And now:

julia> Field2(field=1)
Field2{Int64}
  field: Int64 1
  description: Nothing nothing

However if you do not want to provide the name you can do:

julia> struct Field3{T}
    field::T
    description::Union{String, Nothing}
    Field3(field::T, description=nothing) where T = new{T}(field, description)
end

And now you can just do:

julia> Field3(11)
Field3{Int64}(11, nothing)