# DataTypes as function arguments

**URL:** <https://discourse.julialang.org/t/datatypes-as-function-arguments/3163>\
**Category:** General Usage\
**Tags:** inference\
**Created:** [April 11, 2017, 10:16am UTC](https://discourse.julialang.org/t/datatypes-as-function-arguments/3163 "2017-04-11T10:16:35Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![traktofon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/traktofon/32/591_2.png) [@traktofon](https://discourse.julialang.org/u/traktofon)\
**Post date:** [April 11, 2017, 10:16am UTC](https://discourse.julialang.org/t/datatypes-as-function-arguments/3163/1 "2017-04-11T10:16:35Z")

</div>

Let’s say I want to define a function `tmap` which applies a given function to several arguments, where – this is the tricky part – the arguments are `DataType`s. My first try was the “obvious”

```julia
tmap1(f, xs...) = map(f, xs)

```

which works

```julia
julia> tmap1(zero, Int, Float64, UInt8)
(0, 0.0, 0x00)

```

but the return-type can not be inferred:

```julia
julia> @code_warntype tmap1(zero, Int, Float64, UInt8)
Variables:
  #self#::#tmap1
  f::Base.#zero
  xs::Tuple{DataType,DataType,DataType}

Body:
  begin 
      return (Core.tuple)((f::Base.#zero)((Base.getfield)(xs::Tuple{DataType,DataType,DataType}, 1)::DataType)::Any, (f::Base.#zero)((Base.getfield)(xs::Tuple{DataType,DataType,DataType}, 2)::DataType)::Any, (f::Base.#zero)((Base.getfield)(xs::Tuple{DataType,DataType,DataType}, 3)::DataType)::Any)::Tuple{Any,Any,Any}                                                                                                                                             
  end::Tuple{Any,Any,Any}

```

So I tried the “recursion trick”, as follows:

```julia
tmap2(f, x, xs...) = (f(x), tmap2(f,xs...)...)
tmap2(f) = ()

```

which works only slight better:

```julia
julia> @code_warntype tmap2(zero, Int, Float64, UInt8)
Variables:
  #self#::#tmap2
  f::Base.#zero
  x::Any
  xs::Tuple{DataType,DataType}

Body:
  begin 
      return (Core.tuple)(0, ($(QuoteNode(zero)))((Core.getfield)(xs::Tuple{DataType,DataType}, 1)::DataType)::Any, ($(QuoteNode(zero)))((Core.getfield)(xs::Tuple{DataType,DataType}, 2)::DataType)::Any)::Tuple{Int64,Any,Any}
  end::Tuple{Int64,Any,Any}

```

i.e. return-type inference is still incomplete. In my understanding the inference fails because `typeof(Int)==DataType` instead of `typeof(Int)==Type{Int}` so that the `xs` in `tmap1` and `tmap2` becomes a `Tuple{DataType,...}`, losing the type information. (Correct?)

Now I have come up with a solution using generated functions, as follows (I can’t seem to get the quoting right, so I turned to `Expr`):

```julia
tget{T}(t::Type{Type{T}}) = T # helper function
@generated function tmap3(f, xs...)
    args = Any[Expr(:call, :f, tget(x)) for x in xs]
    Expr(:tuple, args...)
end

```

for which return-type inference succeeds:

```julia
@code_warntype tmap3(zero, Int, Float64, UInt8)
Variables:
  #self#::#tmap3
  f::Base.#zero
  xs::Any

Body:
  begin # line 1:
      return (Core.tuple)(0, (Base.sitofp)(Float64, 0)::Float64, (Base.checked_trunc_uint)(UInt8, 0)::UInt8)::Tuple{Int64,Float64,UInt8}
  end::Tuple{Int64,Float64,UInt8}

```

(I believe the `xs::Any` is harmless.)

This works on 0.5 and 0.6, but I wonder if this is a correct and future-proof approach. As far as I can figure out, it happens to work because during the generation stage of the generated function (in which the arguments are replaced by their types), the argument types are actually `Type{Int}` etc. despite `typeof(Int)==DataType`. Proof:

```julia
julia> @generated function test(xs...); dump(xs); nothing; end
test (generic function with 1 method)

julia> test(Int, Float64, UInt8)
Tuple{DataType,DataType,DataType}
  1: Type{Int64} <: Any
  2: Type{Float64} <: Any
  3: Type{UInt8} <: Any

```

My question is: Is this behaviour something I can rely on, or is this an (accidental?) implementation detail? (Or alternatively, is there a simpler type-stable way of writing `tmap`?)

---

<div class="post-metadata">

**Author:** ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)\
**Post date:** [April 11, 2017, 2:46pm UTC](https://discourse.julialang.org/t/datatypes-as-function-arguments/3163/2 "2017-04-11T14:46:22Z")

</div>

The behavior of generated functions there is [https://github.com/JuliaLang/julia/issues/12783](https://github.com/JuliaLang/julia/issues/12783) and should not be relied upon.
