Stream: helpdesk (published)

Topic: Structs with "optional" fields


view this post on Zulip Davi Sales Barreira (Jan 12 2022 at 13:00):

Consider the following struct using Parameters.jl:

@with_kw struct MyStruct
 color1::String = "white"
 color2::String = "black"
 style1::String = "cool"
 style2::String = "hot"
end

Now, what I'd like to do is to enable the user to create a new instance of this struct with something like:

s = MyStruct(color="blue")

This should be equivalent to s = MyStruct(color1="blue", color2="blue"). Similarly for style, thus,
s = MyStruct(color="red", style="none").

Is there any "elegant" solution for such a problem?

view this post on Zulip Elias Carvalho (Jan 12 2022 at 13:17):

You can use multiple dispatch using NamedTuples:
image.png

view this post on Zulip Patrick Toche (Jan 12 2022 at 13:19):

not that I'm competent to answer, but how about adding a field color and an inner constructor that would check that color1 == color2 to set its value?

view this post on Zulip Davi Sales Barreira (Jan 12 2022 at 14:37):

Patrick Toche said:

not that I'm competent to answer, but how about adding a field color and an inner constructor that would check that color1 == color2 to set its value?

Can you give an example? The thing is, the color field should be optional. It would be just a faster way of attributing color1 and color2.

view this post on Zulip Davi Sales Barreira (Jan 12 2022 at 14:38):

Elias Carvalho said:

You can use multiple dispatch using NamedTuples:
image.png

Thanks, this seems to work. Although, it does not seems to work perfectly, because I have more fields other than color1and color2. And also, I'm using Parameters.jl. The MyStruct(;kwargs...) seems to break the constructor from Parameters.jl

view this post on Zulip Davi Sales Barreira (Jan 12 2022 at 14:50):

Ok, I think I came up with a nice solution:

@with_kw struct _Struct
    color::Union{String,Nothing} = nothing
    color1::String = color === nothing ? "white" : color
    color2::String = color === nothing ? "black" : color
    style::Union{String,Nothing} = nothing
    style1::String = style === nothing ? "blue"  : style
    style2::String = style === nothing ? "white" : style
end

view this post on Zulip Andrey Oskin (Jan 12 2022 at 17:42):

This is overcomplicated, you can just put it in a regular constructor.

view this post on Zulip Andrey Oskin (Jan 12 2022 at 19:37):

For example

struct MyStruct
 color1::String
 color2::String
 style1::String
 style2::String
end

MyStruct(; color = nothing,
           color1 = color === nothing ? "white" : color,
           color2 = color === nothing ? "black" : color,
           style = nothing,
           style1 = style === nothing ? "cool" : style,
           style2 = style === nothing ? "hot" : style) =
    MyStruct(color1, color2, style1, style2)
julia> MyStruct(color = "green", style1 = "warm")
MyStruct("green", "green", "warm", "hot")

view this post on Zulip Davi Sales Barreira (Jan 12 2022 at 21:39):

@Andrey Oskin , your solution didn't work for me :/
The issue is that I'm using Parameters.jl. So things get more complicated.

view this post on Zulip Andrey Oskin (Jan 12 2022 at 21:42):

But Parameters.jl is doing exactly that, it generates keyword constructor. Just instead of relying on macro you can generate it explicitly. There is no need to restrict yourself to Parameters default.


Last updated: Oct 02 2023 at 04:34 UTC