AddToField.jl

Julia macros to easily construct named tuples and set properties of mutable structures. AddToField.jl exports three macros, @addnt, for constructing NamedTuples, @adddict, for creating Dicts with Symbols as keys, and @addto! for modifiying existing data structures. Github repo here.

To create NamedTuples, use @addnt:

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 create Dicts, use @adddict:

julia> using AddToField;

julia> @adddict begin 
           @add a = 1
           @add b = a + 2
       end
Dict{Symbol, Int64} with 2 entries:
  :a => 1
  :b => 3

julia> @adddict begin 
           @add "Variable a" a = 1
           @add b = a + 2
       end
Dict{Symbol, Int64} with 2 entries:
  Symbol("Variable a") => 1
  :b                   => 3

To modify existing structures, use @addto!

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 easier. First, makes the creation of publication-quality tables easier.

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> using DataFrames

julia> @addto! DataFrame() 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
Note

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

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

This is because @addnt, @adddict, and @addto! create anonymous variables, then constructs and modifies objects at the end of the block. The same applies for for loops and functions inside the @addnt, @adddict, 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.