# Concise way to initialize field in terms of another field

**URL:** <https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536>\
**Category:** General Usage\
**Created:** [April 4, 2024, 5:51pm UTC](https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536 "2024-04-04T17:51:07Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)\
**Post date:** [April 4, 2024, 5:51pm UTC](https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536/1 "2024-04-04T17:51:07Z")

</div>

Suppose I have:

```julia
Base.@kwdef struct Example
    a::Int
    b::Int
end

```

I would like the initialization of `b` to refer to the initial value of `a`, but the following does not work:

```julia
x = Example(a = 6, b = a+9)

```

because the “`a`” that appears in the definition of `b` does not refer to the field named `a` but to some external (undefined) `a`. Is there a clean way for the initialization of one field to refer to the value stored in a previously defined field? In C++, the keyword “`this`” addresses this situation.

---

<div class="post-metadata">

**Author:** ![PeterSimon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petersimon/32/25193_2.png) [@PeterSimon](https://discourse.julialang.org/u/PeterSimon)\
**Post date:** [April 4, 2024, 6:21pm UTC](https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536/2 "2024-04-04T18:21:16Z")

</div>

You could define a single-argument constructor method for this:

```julia-repl
julia> Example(a) = Example(a, 9+a)
Example

julia> Example(3)
Example(3, 12)

```

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [April 4, 2024, 6:22pm UTC](https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536/3 "2024-04-04T18:22:09Z")

</div>

it just works, if you write the constructor function manually:

```julia
julia> function f(a,b=1+a)
           a, b
       end
f (generic function with 2 methods)

julia> f(3)
(3, 4)

```

---

<div class="post-metadata">

**Author:** ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)\
**Post date:** [April 4, 2024, 6:35pm UTC](https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536/4 "2024-04-04T18:35:32Z")

</div>

Thanks for the prompt answers to my question! To clarify, the actual `struct` in my code has many fields (more than 20), and the relationships among them do not follow any particular pattern. Hence it would be awkward to solve the problem with custom constructors, especially since the code is under development and the precise relationships between the fields may change over time.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [April 4, 2024, 6:37pm UTC](https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536/5 "2024-04-04T18:37:54Z")

</div>

> [@Stephen\_Vavasis](#):
>
> `x = Example(a = 6, b = a+9)`

at syntax level this will never work, because `a+9` needs to be computed first.

The best you can do is make a macro yourself that would transform this to:

```julia
let a=6, b=a+9
    x = Example(; a, b)
end

```

since it looks like let would resolve it in order:

```julia
julia> let a = 9, b=a+9
           a,b
       end
(9, 18)

```

---

<div class="post-metadata">

**Author:** ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)\
**Post date:** [April 4, 2024, 9:51pm UTC](https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536/6 "2024-04-04T21:51:48Z")

</div>

OK, I tried to code your suggestion, but it didn’t work. As you can see from the run below, the code reproduces the same problem I had originally, namely, that “a” that appears in the assignment statement `b=a+2` does not refer to the “a” that was just initialized. I’m not an expert on macros, and I’m not sure why the macro-expander decided that “a” in one place is a new temporary variable and in the other place is a previously defined variable. Maybe I need to throw in some escapes?

```julia
macro kwconstruct_helper(expr::Expr)
    @assert expr.head == :call
    fname = expr.args[1]
    kwname = Any[]
    kwrhs = Any[]
    numassgn = length(expr.args) - 1
    for j = 2 : numassgn + 1
        @assert expr.args[j].head == :kw
        push!(kwname, expr.args[j].args[1])
        push!(kwrhs, expr.args[j].args[2])
    end
    letassignments = Expr[]
    kwargs = Expr(:parameters)
    for i = 1 : numassgn
        push!(letassignments, Expr(:(=), kwname[i], kwrhs[i]))
        push!(kwargs.args, kwname[i])
    end
    return Expr(:let,
                Expr(:block, letassignments...),
                Expr(:block, __source__ ,
                     Expr(:call, fname, kwargs)))
end

```

Output from

```julia
@macroexpand @kwconstruct_helper Example(a=7, b=a+2)

```

is

```julia
let var"#36#a" = 7, var"#35#b" = Main.test_kwconstruct.a + 2
    #= u:\JuliaFEM\CohesiveFEM\src\kwconstruct_helper.jl:40 =#
    Main.test_kwconstruct.Example(; a = var"#36#a", b = var"#35#b")
end

```

---

<div class="post-metadata">

**Author:** ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)\
**Post date:** [April 5, 2024, 3:53pm UTC](https://discourse.julialang.org/t/concise-way-to-initialize-field-in-terms-of-another-field/112536/7 "2024-04-05T15:53:32Z")

</div>

In case someone returns to this post: The problem with the macro I posted previously is that it is missing some `esc` calls. Here it is again corrected.

```julia
macro kwconstruct_helper(expr::Expr)
    @assert expr.head == :call
    fname = expr.args[1]
    kwname = Any[]
    kwrhs = Any[]
    numassgn = length(expr.args) - 1
    for j = 2 : numassgn + 1
        @assert expr.args[j].head == :kw
        push!(kwname, expr.args[j].args[1])
        push!(kwrhs, expr.args[j].args[2])
    end
    letassignments = Expr[]
    kwargs = Expr(:parameters)
    for i = 1 : numassgn
        push!(letassignments, Expr(:(=), esc(kwname[i]), esc(kwrhs[i])))
        push!(kwargs.args, esc(kwname[i]))
    end
    return Expr(:let,
                Expr(:block, letassignments...),
                Expr(:block, __source__ ,
                     Expr(:call, fname, kwargs)))
end

```

Here is a small test. The statements:

```julia

Base.@kwdef struct Example
    a::Int
    b::Int
end
println(@kwconstruct_helper Example(a=7, b=a+2))

```

produce the output

```julia
Main.test_kwconstruct.Example(7, 9)

```
