# Ridiculous idea: types from the future

**URL:** <https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457>\
**Category:** Internals & Design\
**Tags:** inference\
**Created:** [August 18, 2017, 9:29pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457 "2017-08-18T21:29:03Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [August 18, 2017, 9:29pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/1 "2017-08-18T21:29:03Z")

</div>

I’ve been thinking about a pattern that often comes up when I write Julia code. I frequently find myself creating a vector and then appending elements to that vector within a loop. But that raises the question of how to declare the element type of the vector. Let’s be concrete. Suppose I have a function `f(i)` and I want to call it multiple times and collect the results. I can do the following:

```julia
results = []
for i in 1:10
  push!(results, f(i))
end

```

but this makes `results` a `Vector{Any}`, which is sub-optimal. If the return type of `f(i)` is obvious, then I can use it in my declaration:

```julia
results = T[]
for...
end

```

but often times that return type is not obvious, or is just heavily parameterized and tedious to type out. If I still want a concretely-typed results vector, then I can try to manually invoke `Core.inference`, but that’s not exported and can be tedious, since it requires deriving the types of the inputs to `f()` myself.

There’s another option, which is to use the comprehension syntax:

```julia
[f(i) for i in 1:10]

```

which is perfect for loops with short bodies but less convenient if the loop body is complex.

What I _actually_ want (I think), is to be able to do something like:

```julia
results = <future T>[]
for i in 1:10 
  push!(results, f(i)::<future T>)
end

```

where `<future T>` is a type that has to be figured out based on the type inference of `f(i)` later in the function (thus, in the “future” (not in the world-age sense, though)).

So, my question is: is this possible? Clearly if the inferred type of `f(i)` depends on the type of `results`, then the answer is no. But if the type of `f(i)` is independent of `results`, then it should at least be computable. In a sense, this is just a matter of trying to save the user from having to manually invoke inference.

Anyway, this is all just fairly wild speculation, but I’m curious if others have found themselves wanting something similar or if there’s a way to get what I want without changing Julia itself.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 18, 2017, 10:24pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/2 "2017-08-18T22:24:50Z")

</div>

> [@rdeits](#):
>
> which is perfect for loops with short bodies but less convenient if the loop body is complex.

You can always use `map` with a `do` block:

```julia
results = map(1:10) do i
   ....
end

```

The `map` function already implements the type-narrowing deduction you want. It uses Julia’s type inference as an optimization, but works even when type-inference fails.

---

<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:** [August 19, 2017, 12:00am UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/3 "2017-08-19T00:00:52Z")

</div>

What if you need to build two result arrays in the loop?

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [August 19, 2017, 9:25am UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/4 "2017-08-19T09:25:38Z")

</div>

If there’s only one unknown type, you could

```julia
other_vector = Vector{KnownType}
results = map(1:10) do i
  other_vector[i] = f_other()
  f_res()
end

```

I’ve thought before that it would be cool if functions that returned tuples could somehow be broadcast or mapped into two output vectors, instead of just a vector of tuples.  
I don’t know any way of doing that.

It’s also a slow function:

```julia
julia> @benchmark Core.Inference.return_type(exp, (Float64,))
BenchmarkTools.Trial: 
  memory estimate: 560 bytes
  allocs estimate: 12
  --------------
  minimum time: 6.315 μs (0.00% GC)
  median time: 6.576 μs (0.00% GC)
  mean time: 6.805 μs (2.00% GC)
  maximum time: 1.395 ms (97.65% GC)
  --------------
  samples: 10000
  evals/sample: 5

```

Hundreds of times slower than actually just evaluating this particular function:

```julia
julia> @benchmark exp(4.3)
BenchmarkTools.Trial: 
  memory estimate: 0 bytes
  allocs estimate: 0
  --------------
  minimum time: 18.644 ns (0.00% GC)
  median time: 18.645 ns (0.00% GC)
  mean time: 18.836 ns (0.00% GC)
  maximum time: 41.621 ns (0.00% GC)
  --------------
  samples: 10000
  evals/sample: 997

```

So I think I would evaluate the function once, and then dispatch on that before proceeding and iterating over the remainder of the iterable.  
Another option similar to what rdelts does in the first place:

```julia
results = [f(1)]
for i in 2:10
  push!(results, f(i))
end

```

---

<div class="post-metadata">

**Author:** ![klacru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klacru/32/27890_2.png) [@klacru](https://discourse.julialang.org/u/klacru)\
**Post date:** [August 19, 2017, 2:57pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/5 "2017-08-19T14:57:25Z")

</div>

If the result type of `f` varies, it is no option to determine the element type of the result array in advance.  
I propose, to use an array with most general type as a buffer, and convert to an array of the most specific element type according to the actual sequence of results afterwards.

```julia
julia> results = []
0-element Array{Any,1}
julia> for i in 1:10
         push!(results, rand() < 0.5 ? 42 : 1.0)
       end
julia> collect(promote(results...))
10-element Array{Float64,1}:
  1.0
 42.0
  1.0
...

```

whereas

```julia
julia> map(x -> rand() < 0.5 ? 42 : 1.0, 1:10)
10-element Array{Real,1}:
  1.0
 42  
  1.0
 42  
...

```

---

<div class="post-metadata">

**Author:** ![klacru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klacru/32/27890_2.png) [@klacru](https://discourse.julialang.org/u/klacru)\
**Post date:** [August 19, 2017, 3:14pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/6 "2017-08-19T15:14:18Z")

</div>

Another approach with an own `ArrayBuffer` type, which accumulates the promoted result type:

```julia
julia> mutable struct ArrayBuffer{T}
           a::Array{T}
           et::Type
           ArrayBuffer{T}() where T = new(T[], Union{})
       end

julia> function Base.push!(buf::ArrayBuffer, x)
           buf.et = promote_type(buf.et, typeof(x))
           push!(buf.a, x)
        end

julia> get_result(buf::ArrayBuffer) = (buf.et).(buf.a)
get_result (generic function with 1 method)

julia> buf = ArrayBuffer{Any}()
ArrayBuffer{Any}(Any[], Void)
julia> for i in 1:10
         push!(buf, rand() < 0.5 ? 42 : 1.0)
       end
julia> buf
ArrayBuffer{Any}(Any[1.0, 1.0, 42, 1.0, 42, 1.0, 1.0, 1.0, 42, 42], Float64)

julia> get_result(buf)
10-element Array{Float64,1}:
  1.0
  1.0
 42.
...
```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [August 19, 2017, 6:29pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/7 "2017-08-19T18:29:58Z")

</div>

Thanks for the replies everyone! In particular, the `map()` suggestion from @stevengj solves my particular case quite nicely (although for more complex looping operations it might not work as well).

For comparison, I’ve implemented a few of the options:

```julia
julia> using Base.Test

julia> f(i) = i * 5.0
f (generic function with 1 method)

julia> function f_comprehension()
           [f(i) for i in 1:10]
       end
f_comprehension (generic function with 1 method)

julia> function f_map()
           map(1:10) do i
               f(i)
           end
       end
f_map (generic function with 1 method)

julia> function f_inference()
           T = Core.Inference.return_type(f, (Int,))
           result = T[]
           for i in 1:10
               push!(result, f(i))
           end
           result
       end
f_inference (generic function with 1 method)

julia> mutable struct ArrayBuffer{T}
          a::Array{T}
          et::Type
          ArrayBuffer{T}() where T = new(T[], Union{})
       end

julia> function Base.push!(buf::ArrayBuffer, x)
          buf.et = promote_type(buf.et, typeof(x))
          push!(buf.a, x)
       end

julia> get_result(buf::ArrayBuffer) = (buf.et).(buf.a)
get_result (generic function with 1 method)

julia> function f_arraybuf()
           buf = ArrayBuffer{Any}()
           for i in 1:10
               push!(buf, f(i))
           end
           get_result(buf)
       end
f_arraybuf (generic function with 1 method)

julia> @assert f_comprehension() == f_map() == f_inference() == f_arraybuf()

julia> @inferred f_comprehension();

julia> @inferred f_map();

julia> @inferred f_inference();
ERROR: return type Array{Float64,1} does not match inferred return type Array{_,1} where _
Stacktrace:
 [1] error(::String) at ./error.jl:21

julia> @inferred f_arraybuf()
ERROR: return type Array{Float64,1} does not match inferred return type Any
Stacktrace:
 [1] error(::String) at ./error.jl:21

julia> using BenchmarkTools

julia> @btime f_comprehension();
  43.632 ns (1 allocation: 160 bytes)

julia> @btime f_map();
  46.965 ns (1 allocation: 160 bytes)

julia> @btime f_inference();
  6.660 μs (25 allocations: 1.02 KiB)

julia> @btime f_arraybuf();
  99.434 μs (67 allocations: 2.59 KiB)

```

So the `map` and comprehension versions are inferable and fast, while the versions that rely on run-time promotion or inference are not (not totally surprising, but I actually hoped that `Core.Inference.return_type()` could have been run at compile-time more like a `Base.@pure` function). But I think all of these are useful suggestions for particular cases (in particular, the ArrayBuffer example could be very useful if the resulting array is passed to a function rather than being returned).

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 19, 2017, 6:35pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/8 "2017-08-19T18:35:29Z")

</div>

> [@klacru](#):
>
> I propose, to use an array with most general type as a buffer, and convert to an array of the most specific element type according to the actual sequence of results afterwards.

The implementation of `map` is actually the opposite of this, if I remember correctly. Even if inference returns `Any`, it optimizes for the common case where all the elements are actually the same type by allocating the array based on the type of the first element. As it goes along, it reallocates and copies the array to a wider type if it encounters an element of a different type.

---

<div class="post-metadata">

**Author:** ![klacru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klacru/32/27890_2.png) [@klacru](https://discourse.julialang.org/u/klacru)\
**Post date:** [August 19, 2017, 7:11pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/9 "2017-08-19T19:11:42Z")

</div>

I thought, the missing type-stability of `f` was the problem. I am not surprised to see strong performance advantages for the built-in comprehension and `map` solutions.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 19, 2017, 9:26pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/10 "2017-08-19T21:26:58Z")

</div>

> [@klacru](#):
>
> I am not surprised to see strong performance advantages for the built-in comprehension and map solutions.

The “built-in” `map` function is implemented purely in Julia. There is nothing to stop user code from attaining the same performance, except for the fact that the implementation is admittedly rather subtle.

(`map` actually calls `collect` on a generator, and the core of the implementation is in [Base.collect\_to!](https://github.com/JuliaLang/julia/blob/c54e7ac299e35a06916d49efca710f06f8f0c4e6/base/array.jl#L693-L712).)

---

<div class="post-metadata">

**Author:** ![klacru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klacru/32/27890_2.png) [@klacru](https://discourse.julialang.org/u/klacru)\
**Post date:** [August 20, 2017, 10:52am UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/11 "2017-08-20T10:52:11Z")

</div>

I see the difference! With “built” in I meant, optimized code by experienced implementers.  
I also see, why `map` returns an `Array{Real,1}`, while `get_result` returns a more specific `Array{Float64}` in my example.

Would it be an improvement of `collect_to!` to use `promote_type` instead of `typejoin`?

---

<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:** [August 22, 2017, 3:40am UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/12 "2017-08-22T03:40:32Z")

</div>

> [@rdeits](#):
>
> T = Core.Inference.return\_type(f, (Int,))

The last parameter should be `Tuple{Int}`. At runtime, it sometimes (sort of accidentally) accepts a tuple, but it only infers the latter syntax. This isn’t usually recommended, since `map` is a better API, and currently the preferred way to interact with `Core.Inference.return_type` (when necessary) is via `promote_op` ([Update of Jameson's "common parametric method patterns" by mauro3 · Pull Request #23245 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/23245/files#diff-619ea7b9ee7c2fa87a3720c1361ba46cR665))

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [August 22, 2017, 4:08am UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/13 "2017-08-22T04:08:59Z")

</div>

Thanks! Good to know.

For completeness, here are the fixed versions of my earlier inference example:

```julia
julia> function f_inference_2()
           T = Core.Inference.return_type(f, Tuple{Int})
           result = T[]
           for i in 1:10
               push!(result, f(i))
           end
           result
       end

julia> @inferred(f_inference_2());

julia> @btime f_inference_2();
  215.081 ns (4 allocations: 352 bytes)

julia> function f_promote_op()
           T = Base.promote_op(f, Int)
           result = T[]
           for i in 1:10
               push!(result, f(i))
           end
           result
       end

julia> @inferred(f_promote_op());

julia> @btime f_promote_op();
  171.147 ns (4 allocations: 352 bytes)

```

---

<div class="post-metadata">

**Author:** ![tuckermcclure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tuckermcclure/32/1187_2.png) [@tuckermcclure](https://discourse.julialang.org/u/tuckermcclure)\
**Post date:** [December 5, 2017, 6:19pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/14 "2017-12-05T18:19:13Z")

</div>

It seems that you could wrap material in a _generated function_ to end up with a function customized for whatever type comes along during execution. In fact, now that I’ve gotten into generated functions, I have to say that that ability is one of my favorite parts of Julia. Would this not work for your case?

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [April 6, 2018, 4:55pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/16 "2018-04-06T16:55:04Z")

</div>

I am missing the feature as well. In my use case, it is a simple assembly of a covariance matrix with given floating point precision:

```julia
function pairwise(cov, X)
  n = size(X, 2)
  C = Array{???}(n, n)
  for j=1:n, i=1:n # naive loop for this example
    C[i,j] = cov(X[:,i], X[:,j])
  end
  C
end

```

What I will do is define a function `result_type(cov, X)` to infer the type, similar to what is done in the Distances.jl package. It would be great though if we could just type something like `Array{?}(n, n)` before the loop and have it figured out automatically. C++11 introduced similar functionality under the name of `auto` and `decltype`: [Placeholder type specifiers (since C++11) - cppreference.com](http://en.cppreference.com/w/cpp/language/auto)

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [April 6, 2018, 8:20pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/17 "2018-04-06T20:20:25Z")

</div>

As a note, sometimes (often) you can use a comprehension to achieve the same effect. Although if the iteration body is too big it might not be so pretty. In the given example it might work well though.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [April 6, 2018, 10:03pm UTC](https://discourse.julialang.org/t/ridiculous-idea-types-from-the-future/5457/18 "2018-04-06T22:03:39Z")

</div>

Comprehensions work nicely when the shape of the array is “full”. In the case of covariance, I would like to exploit the symmetry and loop only in entries that aren’t redundant.
