Difference between Type{T} and T when passing type variable inside generated function?

Why passing type argument into @generated function changes its value from T into Type{T}?

function type_print(T)
  return :(println($T))
end

macro type_print(T)
  return :(println($T))
end

@generated function type_print_gen(T)
  return :(println($T))
end

type_print(Int8) |> eval # Int8
@type_print(Int8) # Int8
type_print_gen(Int8) # Type{Int8} - why???

Because of that, I cannot write generated functions that call another functions that use types as input variables, e.g. read(io::IO, T), typemax(T) and so on.

Just wondering, why are you using a generated function? It’s almost never the right choice.

I want to generate the most efficient code to read binary stream into given combination of types. Simply generate repeating reads given a tuple of types. See #2 here, and further performance comparison.

julia> @generated function type_print_gen(T)
         return :(println(T))
       end
type_print_gen (generic function with 1 method)

julia> type_print_gen(Int8)
Int8

EDIT:
But this is probably more along the lines of what you want:

julia> foo(::Type{Type{T}}) where {T} = T
foo (generic function with 1 method)

julia> @generated function type_print_gen(T)
         :(println($(foo(T))))
       end
type_print_gen (generic function with 1 method)

julia> type_print_gen(Int32)
Int32
1 Like

Thank you!
Is there any reason for such specific behaviour?

So my final code looks like this:

foo(::Type{Type{T}}) where {T} = T

# from Tuple{T1, T2, ...} - generate (read(io, T1), read(io, T2), ...)
@generated function read_gen(io::IOBuffer, types::Type...)
    Expr(:tuple, [Expr(:call, :read, :io, foo(i)) for i in types]...)
end

io = IOBuffer(UInt8[0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00])
types = (Int8, Int16, Int32)
read_gen(io, types...)

You can’t interact with values in generated functions, but you can with the types of arguments.
So it’s often more convenient to use the arguments directly (as types) than have to type every argument to the function. This is especially true with varargs.

julia> @generated function foo(a, b...)
           @show a b
       end
foo (generic function with 1 method)

julia> foo((1,0xcf,1f0,1.0), "hi", "world", 3.14, :π, π)
a = Tuple{Int64,UInt8,Float32,Float64}
b = (String, String, Float64, Symbol, Irrational{:π})
(String, String, Float64, Symbol, Irrational{:π})
2 Likes