# ANN: AddToField.jl, a macro to make constructing NamedTuples and setting properties easier

**URL:** https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263
**Category:** Package Announcements
**Created:** [March 30, 2021, 9:28pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263 "2021-03-30T21:28:00Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)
#### Post date: [March 30, 2021, 9:28pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263/1 "2021-03-30T21:28:00Z")

</div>

I’m announcing the release of [AddToField.jl](https://github.com/pdeffebach/AddToField.jl/), a small package with two macros to generate `NamedTuples` from blocks of code.

To create `NamedTuples`, use `@addnt`:

```julia
julia> using AddToField;

julia> @addnt begin 
           @add a = 1
           @add b = a + 2
       end
(a = 1, b = 3)

julia> @addnt begin 
           @add "Variable a" a = 1
           @add b = a + 2
       end
(Variable a = 1, b = 3)

```

To modify existing structures, use `@addto!`

```julia
julia> D = Dict();

julia> @addto! D begin 
           @add a = 1
           @add b = a + 2
           @add "Variable c" c = b + 3
       end
Dict{Any,Any} with 3 entries:
  :a => 1
  :b => 3
  Symbol("Variable c") => 6

```

AddToField makes working with [DataFrames](https://github.com/JuliaData/DataFrames.jl) easier. First, makes the creation of publication-quality tables easier.

```julia
using DataFrames, PrettyTables, Chain
julia> df = DataFrame(
           group = repeat(1:2, 50),
           income_reported = rand(100),
           income_imputed = rand(100));

julia> @chain df begin 
           groupby(:group)
           combine(_; keepkeys = false) do d
               @addnt begin 
                   @add "Group" first(d.group)
                   @add "Mean reported income" m_reported = mean(d.income_reported)
                   @add "Mean imputed income" m_imputed = mean(d.income_imputed)
                   @add "Difference" m_reported - m_imputed
               end
           end
           pretty_table(;nosubheader = true)
       end
┌───────┬──────────────────────┬─────────────────────┬────────────┐
│ Group │ Mean reported income │ Mean imputed income │ Difference │
├───────┼──────────────────────┼─────────────────────┼────────────┤
│ 1 │ 0.523069 │ 0.53696 │ -0.0138915 │
│ 2 │ 0.473178 │ 0.41845 │ 0.0547277 │
└───────┴──────────────────────┴─────────────────────┴────────────┘

```

It also makes constructing data frames easier

```julia
julia> using DataFrames

julia> df = DataFrame();

julia> @addto! df begin
           x = ["a", "b", "c"]
           @add x = x .* "_x"
           @add x_y = x .* "_y"
       end
3×2 DataFrame
 Row │ x x_y    
     │ String String 
─────┼────────────────
   1 │ a_x a_x_y
   2 │ b_x b_x_y
   3 │ c_x c_x_y

```

## Current limitation

You cannot use `@add` in new scopes created with `body`. The following will fail

```julia
@addnt begin
  let
      a = 1
      @add a
  end
end

```

This is because `@addnt` and `@addto!` create anonymous variables, then constructs and modifies objects at the end of the block. The same applies for `for` loops and `function`s inside the `@addnt` and `@addto!` blocks. In theory, `@addto!` should not have this limitation. However I implementing this feature in `@addnt` is more complicated, and At the moment maintaining simple feature parity is important.

I have submitted it for registration and should be available for download in 3 days.

---

<div class="post-metadata">

### Author: ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)
#### Post date: [April 2, 2021, 10:19pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263/2 "2021-04-02T22:19:31Z")

</div>

AddToField.jl is now registered! `] add` it and see if it helps your workflows!

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [April 13, 2021, 5:28pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263/3 "2021-04-13T17:28:57Z")

</div>

This is a really nice little package. I think the simplicity and predictability makes it preferable over StaticModules.jl in many contexts.

I really like the concept of just explicitly declaring what variables get passed out of the named tuple.

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [April 13, 2021, 5:33pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263/4 "2021-04-13T17:33:03Z")

</div>

Can I ask why you opted to do name clobbering rather than just create a `let` block? i.e. currently we have

```julia
@macroexpand @addnt begin 
    @add a = 1
    @add b = a + 2
    c = 2a + b
end

#+RESULTS:
quote
    begin
        #= In[32]:2 =#
        begin
            var"##283" = (a = 1)
        end
        #= In[32]:3 =#
        begin
            var"##284" = (b = a + 2)
        end
        #= In[32]:4 =#
        c = 2a + b
    end
    (; :a => var"##283", :b => var"##284")
end

```

but wouldn’t it be better for this to produce instead

```julia
quote
    let
        #= In[32]:2 =#
        a = 1
        #= In[32]:3 =#
        b = a + 2
        #= In[32]:4 =#
        c = 2a + b
    end
    (; a = a, b = b)
end

```

?

---

<div class="post-metadata">

### Author: ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)
#### Post date: [April 13, 2021, 5:38pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263/5 "2021-04-13T17:38:25Z")

</div>

I opted for the clobbering for two reasons

First, I was worried about re-assignment. For clarity, I thought that adding the value exactly as it was at the `@add` command would be the most intuitive.

```julia
@addnt begin 
    @add x = 1
    x = 200
end

```

I really wanted to avoid some sort of container, like a `Dict` which stored values behind the scenes, since that could have performance drawbacks. So the only container to really use was just anonymous variables.

Second, a `let` seems like a slightly orthogonal construct for this. You shouldn’t need to make a new scope just to add a named tuple. Plus a major feature of `@add x = 1` is that `x` could be used later. Given I like that feature so much, it seemed limiting to make `x` disappear after the named tuple was created.

EDIT: There is also the edge case of `@add "non valid identifier" heavy_function()` which needs an anonymous variable anyways.

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [April 13, 2021, 5:49pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263/6 "2021-04-13T17:49:14Z")

</div>

I was about to say “okay, I guess I can just wrap it in a `let` block myself”, but then I realized to my delight that there’s nothing about your implementation that demands I use `begin` at all!

```julia
@macroexpand @addnt let
    @add a = 1
    @add b = a + 2
    c = 2a + b
end

#+RESULTS:
quote
    let
        #= In[37]:2 =#
        begin
            var"##294" = (a = 1)
        end
        #= In[37]:3 =#
        begin
            var"##295" = (b = a + 2)
        end
        #= In[37]:4 =#
        c = 2a + b
    end
    (; :a => var"##294", :b => var"##295")
end

```

I like this a lot!

---

<div class="post-metadata">

### Author: ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)
#### Post date: [April 13, 2021, 5:49pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263/7 "2021-04-13T17:49:59Z")

</div>

Who knew!

Ill make sure to add that to the docs.

---

<div class="post-metadata">

### Author: ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)
#### Post date: [June 21, 2021, 6:41pm UTC](https://discourse.julialang.org/t/ann-addtofield-jl-a-macro-to-make-constructing-namedtuples-and-setting-properties-easier/58263/8 "2021-06-21T18:41:43Z")

</div>

I just tagged version `0.1.1` of AddToField.jl which implements the above `let` block usage.

```julia
julia> @addnt begin 
           @add x = 1
           y = 5
       end
(x = 1,)

julia> y
5

julia> @addnt let 
           @add x = 1
           z = 5
       end
(x = 1,)

julia> z
ERROR: UndefVarError: z not defined

```
