Search code examples
dataframejuliametaprogramming

Expr eval within Julia module gives UndefVarError


I'm able to run the following code just fine and it provides the expected result:

julia> using DataFrames, DataFramesMeta

julia> expr = "2*:a+:b"
"2*:a+:b"

julia> df = DataFrame(a=[1,2],b=[3,4])
2×2 DataFrame
 Row │ a      b     
     │ Int64  Int64 
─────┼──────────────
   1 │     1      3
   2 │     2      4

julia> eval(Meta.parse("@transform(df, " * join(collect(":res" => expr), " = ") * ")"))
2×3 DataFrame
 Row │ a      b      res   
     │ Int64  Int64  Int64 
─────┼─────────────────────
   1 │     1      3      5
   2 │     2      4      8

However, this fails when done inside a module.

julia> module foo
           using DataFrames, DataFramesMeta
           function bar()
               expr = "2*:a+:b"
               df = DataFrame(a=[1,2],b=[3,4])
               eval(Meta.parse("@transform(df, " * join(collect(":res" => expr), " = ") * ")"))
           end
       end
Main.foo

julia> foo.bar()
ERROR: UndefVarError: df not defined
Stacktrace:
 [1] top-level scope
   @ ~/.julia/packages/DataFramesMeta/BkRtJ/src/macros.jl:1363
 [2] eval
   @ ./boot.jl:368 [inlined]
 [3] eval
   @ ./REPL[9]:1 [inlined]
 [4] bar()
   @ Main.foo ./REPL[9]:6
 [5] top-level scope
   @ REPL[10]:1

I imagine this is possible scope issue, and tried to be explicit by using foo.df instead of df in the function call, but without success.

Would anyone know how what is preventing df from being recognized as a defined variable here?


Solution

  • Like Bogumil said eval totally does not look like a recommended way to write your code.

    However if your goal is learning macros this is the correct code:

    module Foo2
        using DataFrames, DataFramesMeta
        function bar()
            expr = "2*:a+:b"
            df = DataFrame(a=[1,2],b=[3,4])
            code = quote
                @transform($df, :res = $(Meta.parse(expr)))
            end
            eval(code)
        end
    end
    

    Now you can run:

    julia> Foo2.bar()
    2×3 DataFrame
     Row │ a      b      res
         │ Int64  Int64  Int64
    ─────┼─────────────────────
       1 │     1      3      5
       2 │     2      4      8