How to construct a type using Meta programming?

I would like to contruct a type using meta programming. More specific,I need a tuple type like

Tuple{Vector{String}, Vector{String}}

.
Constructing a value is quite easily possible,
e.g.

ex = Expr(:tuple)
push!(ex.args, 1)
push!(ex.args, 2)

with

eval(ex)

resulting in the tuple value

(1,2)

But how to construct a tuple type?
I used dump to get a syntax tree of the tuple type mentioned above.

dump(:(Tuple{Vector{String}, Vector{String}}))

It results in the following syntax tree:

Expr
  head: Symbol curly
  args: Array{Any}((4,))
    1: Symbol Tuple
    2: Expr
      head: Symbol curly
      args: Array{Any}((2,))
        1: Symbol Vector
        2: Symbol String
    3: Expr
      head: Symbol curly
      args: Array{Any}((2,))
        1: Symbol Vector
        2: Symbol String
    4: Expr
      head: Symbol curly
      args: Array{Any}((2,))
        1: Symbol Vector
        2: Symbol String

However, using the symbol curly, using

ex = Expr(:curly)

did not work to get a well-formed expression so far.
I would be very happy if you had a suggesion.

Expr(:curly, :Tuple, :Int, :Char) works just fine to construct :(Tuple{Int, Char}), for example.

PS. Obligatory warning that metaprogramming is the wrong tool 99% of the time … but is very useful in remaining 1%.

2 Likes

First, I’ll echo the above that there might be a better way to do this without metaprogramming. Metaprogramming is difficult to write and read and it can be brittle.

To answer your specific query based on the dump you gave (less args[4], which should not actually be present):

julia> Expr(:curly, :Tuple, Expr(:curly, :Vector, :String), Expr(:curly, :Vector, :String))
:(Tuple{Vector{String}, Vector{String}})

or, for an incremental build:

julia> ex = Expr(:curly, :Tuple); for _ in 1:2; push!(ex.args, Expr(:curly, :Vector, :String)); end; ex
:(Tuple{Vector{String}, Vector{String}})

julia> ex = :(Tuple{}); for _ in 1:2; push!(ex.args, :(Vector{String})); end; ex # quotes are usually nicer
:(Tuple{Vector{String}, Vector{String}})

Deconstructing the above a bit, notice that :curly needs an args[1] to tell it what is curly to make a “well-formed expression”:

julia> ex = Expr(:curly); push!(ex.args, :Tuple); ex
:(Tuple{})