Interpolating symbol in @eval expression

In this example, I’m trying to define functions for accessing data frame columns. However, the interpolation within the getindex call ended up with a type rather than the original symbol… Why is that? What can I do to fix it?

julia> module Foo
           using DataFrames
           for f in (:x, :y)
               @eval $(f)(df::AbstractDataFrame) = df[:, $f]
           end
       end
Main.Foo

julia> df = DataFrame(x = rand(3), y = rand(3))
3×2 DataFrame
│ Row │ x        │ y        │
│     │ Float64  │ Float64  │
├─────┼──────────┼──────────┤
│ 1   │ 0.206567 │ 0.238623 │
│ 2   │ 0.467982 │ 0.219791 │
│ 3   │ 0.219056 │ 0.158868 │

julia> Foo.x(df)
ERROR: MethodError: no method matching getindex(::DataFrame, ::Colon, ::typeof(Main.Foo.x))
Closest candidates are:
  getindex(::DataFrame, ::Colon) at /Users/tomkwong/.julia/packages/DataFrames/0Em9Q/src/dataframe/dataframe.jl:306
  getindex(::DataFrame, ::Colon, ::Colon) at /Users/tomkwong/.julia/packages/DataFrames/0Em9Q/src/dataframe/dataframe.jl:381
  getindex(::DataFrame, ::Colon, ::Union{Signed, Symbol, Unsigned}) at /Users/tomkwong/.julia/packages/DataFrames/0Em9Q/src/dataframe/dataframe.jl:358

Interestingly, getproperty works. I still don’t understand why the other syntax failed though…

julia> module Foo
           using DataFrames
           for f in (:x, :y)
               @eval $(f)(df::AbstractDataFrame) = df.$(f)
           end
       end
Main.Foo

julia> Foo.x(df)
3-element Array{Float64,1}:
 0.20656716839978007
 0.4679817773900221 
 0.21905643202268887

No it did not end up with a type.

Simply shows you the type. The value you pass in is of type typeof(Main.Foo.x), i.e. you passed in Main.Foo.x.

This is because what you write is simply x(df::AbstractDataFrame) = df[:, x], which you can see yourself by printing the result of the interpolation. You probably want x(df::AbstractDataFrame) = df[:, :x] instead which means that you should interpolate the quoted symbol rather than passing in the symbol directly. You can figure out what to pass in by looking at :(:x) and in this case one way to do it is $(QuoteNode(f))

3 Likes

Perfect, thanks!