# How to add a property for all custom structs?

**URL:** <https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266>\
**Category:** New to Julia\
**Tags:** struct\
**Created:** [November 15, 2022, 12:01am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266 "2022-11-15T00:01:57Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![iHany](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihany/32/18151_2.png) [@iHany](https://discourse.julialang.org/u/iHany)\
**Post date:** [November 15, 2022, 12:01am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/1 "2022-11-15T00:01:57Z")

</div>

Hi!  
I made my custom types as below.  
As can be seen in the script, I’d like to impose a new property `c` to all types which are subtypes of `AbstractMine`.  
Should I change all of the definitions of the subtypes?  
Or, is there any better way?

Note that the types correspond to neural networks and I wanna add normalization factors to each network.  
If you have a better idea for this, please let me know!

````julia
struct MyA <: AbstractMine
    a::Int
end

struct MyB <: AbstractMine
    b::Int
end

# I'd like to make a new property, namely `c`, for all `Mine::AbstractMine`'s.
# For example,
# ```
# modified_my_a = MyA(1, 2)
# modified_my_a.c == 2 # true
# ```

````

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [November 15, 2022, 2:55am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/2 "2022-11-15T02:55:52Z")

</div>

There is not a way to do this directly.

One mechanism is to use a macro.

```julia
julia> macro addc(ex)
           p = ex.args[3]
           push!(p.args, :(c::Int))
           ex
       end
@addc (macro with 1 method)

julia> abstract type AbstractMine end

julia> @addc struct MyA <: AbstractMine
           a::Int
       end

julia> @addc struct MyB <: AbstractMine
           b::Int
       end

julia> fieldnames(MyA)
(:a, :c)

julia> fieldnames(MyB)
(:b, :c)

```

---

<div class="post-metadata">

**Author:** ![iHany](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihany/32/18151_2.png) [@iHany](https://discourse.julialang.org/u/iHany)\
**Post date:** [November 15, 2022, 3:02am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/3 "2022-11-15T03:02:36Z")

</div>

Ah, yeah, using the macro will surely work.

Thanks!

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [November 15, 2022, 3:11am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/4 "2022-11-15T03:11:49Z")

</div>

Although there’s not much difference in effort between adding `@addc` and adding `c::Int` to your struct definitions. I would just update the struct definitions.

---

<div class="post-metadata">

**Author:** ![iHany](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihany/32/18151_2.png) [@iHany](https://discourse.julialang.org/u/iHany)\
**Post date:** [November 15, 2022, 3:14am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/5 "2022-11-15T03:14:06Z")

</div>

That makes sense 🙂  
But if I need to add several properties for all structs, then using macro would be better in terms of readability and the reduction of the efforts.  
I need to think about it for a bit. Thx!

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [November 15, 2022, 4:44am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/6 "2022-11-15T04:44:16Z")

</div>

I should mention there are several packages which make this easier:

> **[GitHub - Suzhou-Tongyuan/ObjectOriented.jl: Conventional object-oriented...](https://github.com/Suzhou-Tongyuan/ObjectOriented.jl)**
>
> Conventional object-oriented programming in Julia without breaking Julia's core design ideas - GitHub - Suzhou-Tongyuan/ObjectOriented.jl: Conventional object-oriented programming in Julia with...

> **[GitHub - ipod825/OOPMacro.jl: Object Oriented Programming in Julia](https://github.com/ipod825/OOPMacro.jl)**
>
> Object Oriented Programming in Julia. Contribute to ipod825/OOPMacro.jl development by creating an account on GitHub.

Here’s a deluxe version of the macro I started above:

```julia
julia> macro extendsMine(ex)
           # Make struct subtype AbstractMine
           name = ex.args[2]
           ex.args[2] = :($name <: AbstractMine)
           
           # Add some base fields
           fields = ex.args[3]
           push!(fields.args, :(c::Int))
           push!(fields.args, :(d::Float64))
         
           get_c = esc(:get_c)
           get_d = esc(:get_d)
           
           quote
               $ex
               # Add some getter methods
               $get_c(s::$name) = s.c
               $get_d(s::$name) = s.d
           end
       end
@extendsMine (macro with 1 method)

julia> abstract type AbstractMine end

julia> @extendsMine struct MyA
           a::Int
       end
get_d (generic function with 1 method)

julia> @extendsMine struct MyB
           b::Int
       end
get_d (generic function with 2 methods)

julia> MyA(1,2,3)
MyA(1, 2, 3.0)

julia> MyB(1,2,3)
MyB(1, 2, 3.0)

julia> get_c(ans)
2

julia> fieldnames(MyA)
(:a, :c, :d)

julia> fieldnames(MyB)
(:b, :c, :d)

julia> methodswith(MyA)
[1] get_c(s::MyA) in Main at REPL[1]:17
[2] get_d(s::MyA) in Main at REPL[1]:18

julia> methodswith(MyB)
[1] get_c(s::MyB) in Main at REPL[1]:17
[2] get_d(s::MyB) in Main at REPL[1]:18

julia> get_c(MyA(1,2,3))
2

julia> get_d(MyB(1,2,3))
3.0

```

Let’s discuss how one figures this out.

The first step is turn the starting code into an expression object. Then we probe the head, the args, and then recursively descend until we have identified all the components that we want to manipulate.

```julia
julia> ex = :(struct MyA <: AbstractMine
           a::Int
       end)
:(struct MyA <: AbstractMine
      #= REPL[15]:2 =#
      a::Int
  end)

julia> typeof(ex)
Expr

julia> ex.head
:struct

julia> ex.args
3-element Vector{Any}:
 false
      :(MyA <: AbstractMine)
      quote
    #= REPL[15]:2 =#
    a::Int
end

julia> ex.args[2]
:(MyA <: AbstractMine)

julia> ex.args[2].head
:<:

julia> ex.args[2].args
2-element Vector{Any}:
 :MyA
 :AbstractMine

julia> ex.args[3]
quote
    #= REPL[15]:2 =#
    a::Int
end

julia> ex.args[3].head
:block

julia> ex.args[3].args
2-element Vector{Any}:
 :(#= REPL[15]:2 =#)
 :(a::Int)

julia> ex.args[3].args[2]
:(a::Int)

julia> ex.args[3].args[2].head
:(::)

julia> ex.args[3].args[2].args
2-element Vector{Any}:
 :a
 :Int

julia> supertype(MyA)
AbstractMine

julia> supertype(MyB)
AbstractMine

julia> subtypes(AbstractMine)
2-element Vector{Any}:
 MyA
 MyB

```

In this case, I wanted to make the subtyping part of the macro, so I needed to compare this a struct definition that is not subtyped.

```julia
julia> ex_simple = :(struct MyB
           b::Int
       end)
:(struct MyB
      #= REPL[28]:2 =#
      b::Int
  end)

julia> ex_simple.args[2]
:MyB

julia> typeof(ex_simple.args[2])
Symbol

```

Armed with the knowledge obtained above, I see that we need to replace argument 2 with an `Expr`, and then we need to push some additional expressions into the field block. Lastly I need to create a new quote block to add some new escaped expressions.

Have fun!

---

<div class="post-metadata">

**Author:** ![iHany](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihany/32/18151_2.png) [@iHany](https://discourse.julialang.org/u/iHany)\
**Post date:** [November 17, 2022, 5:29am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/7 "2022-11-17T05:29:09Z")

</div>

Alright, after a bit of thinking,  
I ended up making a new type, namely, `NormalizedMine` and construct it as follows,

```julia
struct NormalizedMine
    nn::AbstractMine
    normalization_factor
end

```

which seems better for me for now.

---

<div class="post-metadata">

**Author:** ![jmair](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jmair/32/35117_2.png) [@jmair](https://discourse.julialang.org/u/jmair)\
**Post date:** [November 17, 2022, 9:00am UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/8 "2022-11-17T09:00:12Z")

</div>

It’s often a good idea to extend functionality like this. You might be interested in the `@forward` macro from the `Lazy.jl` package.

Also be careful of your types here, you might not get specialisation due to the abstract type. Consider using a generic:

```julia
struct NormalizedMine{T<:AbstractMine,Q}<:AbstractMine
    nn::T
    normalization_factor::Q
end

```

You don’t need the `Q` type, especially if you know the concrete type already.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [November 17, 2022, 8:24pm UTC](https://discourse.julialang.org/t/how-to-add-a-property-for-all-custom-structs/90266/9 "2022-11-17T20:24:58Z")

</div>

Definitely do what @jmair recommends.

Also, to hide the implementation details you can definte “getter” methods and use that rather than accessing fields directly.

```julia
c(mine::NormalizedMine) = mine.nn.c

```

Alternatively, you can implement `Base.getproperty`:

```julia
Base.getproperty(mine::NormalizedMine, s::Symbol) = s == :c ? mine.nn.c : getfield(mine, s)

```
