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.

2 Likes

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.

1 Like

This is one of the options indeed.

1 Like

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.

2 Likes

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.

1 Like

Many thanks! :pray: