Can I make Julia eval a variable before using it as a kwargs?

Hi! I’m building a DataFrame with the column names stored in some variables (both as symbols and strings):

adc_I_title = "Arc ADC Current (A)"
adc_V_title = "Arc ADC Voltage (V)"
adc_I_symbol = Symbol(adc_I_title)
adc_V_symbol = Symbol(adc_V_title)

Now I want to build a DataFrame that has those column names. I cannot use the usual kwargs for this:

julia> DataFrame(adc_I_symbol = 3, adc_V_symbol = 2)
1×2 DataFrame
 Row │ adc_I_symbol  adc_V_symbol 
     │ Int64         Int64        
─────┼────────────────────────────
   1 │            3             2

but I need to use one of the other constructors, using arrays:

julia> DataFrame([[3], [2]], [adc_I_symbol, adc_V_symbol])
1×2 DataFrame
 Row │ Arc ADC Current (A)  Arc ADC Voltage (V) 
     │ Int64                Int64               
─────┼──────────────────────────────────────────
   1 │                   3                    2

Now, since this may be something potentially useful in other settings, I wondered: can I make Julia eval an argument before passing it to a function? Much like a Lisp’s (eval adc_I_symbol) form.

Thanks!

My guess (as someone who don’t really have a clue) is that eval does not work in that position, and the way you could achieve this would be some macro that transforms the first form into the second.

You could write a macro that would do this, but the recommended pattern is to convert strings and symbols to a common type (either string or symbol - it does not matter) and use the constructors that allow for programmatic passing of column names.

I think Bogumil is referring to this:

julia> DataFrame(adc_I_symbol => 3, adc_V_symbol => 2)
1×2 DataFrame
 Row │ Arc ADC Current (A)  Arc ADC Voltage (V)
     │ Int64                Int64
─────┼──────────────────────────────────────────
   1 │                   3                    2

(note the => instead of =) - as long as you pass only strings or symbols you’re fine to use the variable identifiers in the constructor.

This is one of the options indeed.

The general Julia way to pass a symbol as kwarg name is just:

# define f(...) for this example:
julia> f(; kwargs...) = kwargs

julia> f(; adc_I_symbol => 1)
pairs(::NamedTuple) with 1 entry:
  Symbol("Arc ADC Current (A)") => 1

Works with any function/constructor.

Thanks to all! @nilshg I missed the possibility to use a Pair as kwargs. However, a minor question for @aplavin : what do you mean when you say “Works with any function/constructor”? From your example, I see the printed kwarg is the entire Pair, so it “works” only if there is a method dealing with a Pair argument in the first place, right?

It works with arbitrary kwarg functions. My example functions just printed all kwargs :slight_smile:

For example:

# use adc_I_symbol as column name
julia> StructArray(adc_I_symbol=1:3)
3-element StructArray(::UnitRange{Int64}) with eltype NamedTuple{(:adc_I_symbol,), Tuple{Int64}}:
 (adc_I_symbol = 1,)
 (adc_I_symbol = 2,)
 (adc_I_symbol = 3,)

# use value of adc_I_symbol as column name
julia> StructArray(;adc_I_symbol=>1:3)
3-element StructArray(::UnitRange{Int64}) with eltype NamedTuple{(Symbol("Arc ADC Current (A)"),), Tuple{Int64}}:
 (var"Arc ADC Current (A)" = 1,)
 (var"Arc ADC Current (A)" = 2,)
 (var"Arc ADC Current (A)" = 3,)

Ok, but I mean, in your last example, does StructArray have a method expecting a Pair, or is it a feature of Julia that all kwargs, if they are Pairs, are treated this way? If yes, where in the docs can I read more about this?

No, there is no way to specifically craft a method that distinguishes kwargs passed like this (as pairs) from regular kwargs.
It’s right there in kwargs docs at Functions · The Julia Language

One can also pass key => value expressions after a semicolon. For example, plot(x, y; :width => 2) is equivalent to plot(x, y, width=2). This is useful in situations where the keyword name is computed at runtime.

Many thanks! :pray: