Splicing a full type name into expression


#1

I have the following type inside a module:

module M struct T end end

Then the following works as expected, yielding an instance of M.T:

eval(:(M.T()))

Using the type indirectly also works, yielding again an instance of M.T:

stype = M.T
eval(:($stype()))

However, dumping the expression for indirect approach:

dump(:($stype()))

Expr
  head: Symbol call
  args: Array{Any}((1,))
    1: M.T <: Any
  typ: Any

it becomes obvious that it incorporated the entire type into expression tree. Comparing to the direct way:

dump(:(M.T()))

Expr
  head: Symbol call
  args: Array{Any}((1,))
    1: Expr
      head: Symbol .
      args: Array{Any}((2,))
        1: Symbol M
        2: QuoteNode
          value: Symbol T
      typ: Any
  typ: Any

And here we got a well-formed expression tree.
Question: how to achieve the same effect (a well-formed expression tree) using the indirect approach, through the stype variable?


#2

There’s nothing not well formed about it.


#3

But there is no easy way to achieve the exact correspondence between the trees? For my purposes the behavior of the :(M.T()) is more desirable than that of :($stype()), but I need to use the dynamically supplied stype::DataType variable.


#4

It’s unclear what do you want with :(M.T()) since it’s not really well defined. Or there’s basically no robust way to construct a expression with only symbols that can reliably refer to the right object in all evaluation context.

Another way to do it reliably would be :($M.T()).

If you want an expression that has only symbols you can have a look at https://github.com/JuliaLang/julia/blob/81e245c850b18d871dbbfc6eebf21d129030ae53/test/testenv.jl#L31. That code assume a particular evaluation context that’s useful for test (or mainly for printing purpose).


#5

Thank you for the answer, I don’t want anything with :(M.T()), I do want to (and need to) use :($stype()). The code you pointed me at only shows how to get the name of the current module. Is there any function which allows to get the module in which a type is defined? In this case I can construct Symbol-only expression manually, this seems to be the only way.


#6
julia> @which DateTime                                                                                                                                                   
Base.Dates                                                                                                                                                               

#7

Thank you for the answer, @mauro3, but this doesn’t work really:

stype = M.T
@which stype
# output:
Main

So it returns a module in which module M is defined, in this case Main. I’m asking about module in which the type T is defined.


#8

Yep, you’re right, it gives the module in which the binding is defined. This works stype.name.module.


#9

There’s nothing wrong with inserting literal objects into expressions, like :($stype) or :($M.stype). I do it all the time. Why do you want to avoid it? If you construct :(ModuleName.type), you’ll have problems in places where ModuleName is not imported, or when it’s shadowed by a local variable.


#10

@cstjean I was surprised actually that it works at all, and thought that this is not a correct way of doing this.
So what happens to this literal object when

  • expression is evaluated? Literal objects are just inserted in right places, while others constructed from symbolic representations? In this case the expression acts as a closure, allowing to insert objects from elsewhere?
  • expression is converted to a string (if you want some generated code to stay as a physical file)?

#11

@mauro3 thanks for the tip, this indeed will allow to express a DataType in symbolic form. Btw, how to find out things like this (that DataType has a field name)? Documentation explorer yielded nothing about DataType and its internals, fieldnames() returns fields of the DataType itself.


#12

That works exactly as you would expect. In fact, it simplifies the compiler’s job, since it knows exactly what object is there, instead of having to look it up. It’s just a pointer to an object - there’s no memory issue.

If you just want to print it, it kinda works, but obviously if you want to eval/parse that code, you’re likely to have problems with some objects (types are probably fine?). You could also try JLD.jl/JLD2.jl instead of printing it.


#13

fieldnames(DataType) works. Also dump is of use. But often hitting Tab works. This is what I did:

julia> Int.  # Tab Tab                                                                                                                                                            
abstract         hasfreetypevars   isleaftype        llvm::DIType      mutable           ninitialized      size              types                                       
depth            instance          layout            llvm::StructType  name              parameters        super             uid                                         
julia> Int.name.      # Tab Tab                                                                                                                                                   
cache       hash         linearcache  module       mt           name         names        wrapper                                                                        
julia> Int.name.module                                                                                                                                                   
Core                                                                                                                                                                     

Aside, note that these internals may change without deprecation from one version to the next.