Given an object, return Julia code that defines the object

Is there a function or macro that accepts an object and returns the Julia code that defines the object? For instance, is there a function f() with the following behaviour?

julia> X=Dict([("A", 1), ("B", 2)])
Dict{String, Int64} with 2 entries:
  "B" => 2
  "A" => 1
julia> f(X)
Dict([("A", 1), ("B", 2)])
Dict{String, Int64} with 2 entries:
  "B" => 2
  "A" => 1

My use case is software regression testing, so any alternative for recovering a definition from a value is welcome.

uhh, depends on what you mean.

  1. If you mean I want an already assembled call to the default constructor, then show(X) gives you Dict("B" => 2,"A" => 1) in your example.
  2. If you mean I want to recover the exact way this object was constructed or the line this happened, no. I do not believe this is easy to achieve and, in fact, it may be impossible.

try this:

julia> repr(x)
"Dict(\"B\" => 2, \"A\" => 1)"

julia> Meta.parse(repr(x))
:(Dict("B" => 2, "A" => 1))

julia> eval(Meta.parse(repr(x)))
Dict{String, Int64} with 2 entries:
  "B" => 2
  "A" => 1

in general repr is designed such that if you copy paste it into REPL it would work, but this is not guaranteed.

As you can see already, because there exist multiple way to construct identical Dict(), you can’t inverse the process.

Are you sure this is not a XY problem? what is that you want to test?

1 Like

Option 1 is sufficient. I just need a way to reproduce the result, not recover the history. show() works for dictionaries, but not for other objects like DataFrames. For these, it behaves more like display, which gives a human-readable (but not Julia-readable) version.

I am writing a function that manipulates DataFrames. A better example might be:

julia> Y=DataFrame(a=[1,2,3],b=['q','r','s'])
3Γ—2 DataFrame
 Row β”‚ a      b    
     β”‚ Int64  Char 
   1 β”‚     1  q
   2 β”‚     2  r
   3 β”‚     3  s

julia> Y[!,:c] = [true, true, false];

julia> show(Y)
3Γ—3 DataFrame
 Row β”‚ a      b     c     
     β”‚ Int64  Char  Bool  
   1 β”‚     1  q      true
   2 β”‚     2  r      true
   3 β”‚     3  s     false

I am looking for a function with this behaviour:

julia> f(Y)
DataFrame(a=[1,2,3],b=['q','r','s'],c=[true, true, false])

but why? what is the property you can’t test without a string that evaluates to some dataframe you already have?

There is none. As a workaround, I could type out the definition myself. I am curious if there is some way to generate this automatically. This would be helpful for much larger structures.

I’m confused, I thought you already have an object in the flow of your program, that you want to invert back to String.

does the functionality you’re testing manipulate the source code string itself??

The function I write will generate the object. However, I am software regression testing, so I want to make sure that the output stays the same between versions. So, I’d like to save the output from a previous version. Test cases might involve very large data frames, so it isn’t feasible to type out the definition of the intended output. One workaround might be to compare the test display of the DataFramse using show(), but I’d like to directly compare DataFrames for equality.

just save the output to a string, re-direct stdout, and compare? this is a very common test in Julia

how else are you gonna test across versions? if you use some auto generation, such as repr, supposedly it will always equal to the other end of the equality right? namely, the current repr you want to test.

so you always have to hard-code previous, known good output – isn’t that the whole point?

Thanks, this should be sufficient. Still, I’d prefer to hard code two DataFrame definitions rather than hard code the text output from the display. Since Julia is homoiconic, I’d be interested in seeing a version of repr() or show() that handles a DataFrame the way a Dict is handled. I’ll accept your answer if I can’t find something like that. Many thanks!

julia> let df = DataFrame(a=[1]); invoke(show, Tuple{typeof(stdout), Any}, stdout, df) end
DataFrame(AbstractVector[[1]], DataFrames.Index(Dict(:a => 1), [:a]))

DataFrames.jl could probably make this easier, perhaps with repr . cc @bkamins