# Most elegant way to create a vector of results if the result is an unknown type?

**URL:** <https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906>\
**Category:** General Usage\
**Tags:** julian-way, api\
**Created:** [September 26, 2024, 10:07am UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906 "2024-09-26T10:07:31Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Maurizio\_Tomasi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maurizio_tomasi/32/384_2.png) [@Maurizio\_Tomasi](https://discourse.julialang.org/u/Maurizio_Tomasi)\
**Post date:** [September 26, 2024, 10:07am UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906/1 "2024-09-26T10:07:31Z")

</div>

Hi all, I am developing some simulation code that can be used either to do a quick calculation or a more complete and slower algorithm. I implemented two functions that either produce a “simple” or “complex” structure; for the sake of simplicity, let’s assume that the calculations are very trivial:

```julia
abstract type Results{T <: Number} end

struct SimpleResults{T} <: Results{T}
    val1::T
    val2::T
end

simple(input) = SimpleResults(input, input * 2)

struct ComplexResults{T} <: Results{T}
    val1::T
    val2::T
    val3::T
end

complex(input) = ComplexResults(input, input * 2, input * 3)

```

Both return types are derived from the abstract type `Results`.

In a typical case, these calculations should be performed many times in a row, producing a long vector of results. For these, I implemented a `ListOfResults` type that basically wraps a `AbstractVector{<: Results}` and keeps some ancillary information too (in `info`):

```julia
struct ListOfResults{R <: Results, V <: AbstractVector{R}}
    vec::V
    info::String
end

```

I am now a bit puzzled in thinking how to create a proper API for the function that creates a `ListOfResults` object. I would like to let the user decide whether `simple` or `complex` should be called and which vector-like use for `ListOfResults.vec` (`Vector`, `CuArray`, …).

Since the exact `eltype` of `vec` is decided only _after_ a call to `simple` or `complex` is done, my strategy would be something like this:

```julia
function fill_list_of_results(list_of_inputs, array_type, fn_to_call)
    # Query the first result
    first_result = fn_to_call(list_of_inputs[begin])

    # Allocate the array
    R = typeof(first_result)
    V = array_type{R}
    vec = V(undef, length(list_of_inputs))

    # Build the object to be returned
    result = ListOfResults(vec, "some useful information")

    # Fill the array
    result.vec[begin] = first_result
    for (cur_idx, cur_input) in enumerate(list_of_inputs)
        (cur_idx == firstindex(list_of_inputs)) && continue

        result.vec[cur_idx] = fn_to_call(cur_input)
    end

    return result
end

```

This implementation works as expected:

```julia
# Works!
fill_list_of_results([1, 2, 3], Vector, simple)

# Works too!
fill_list_of_results([1.0, 2.0, 3.0], Vector, complex)

```

However, I am not entirely satisfied with it. I don’t find the implementation of `fill_list_of_results` too elegant nor “Julia”-like: it calls the function once, saves the result back and uses the type to build an array, then does a `for` loop skipping the first element… It’s more complicated than I would have expected, but I am at loss to find a smarter way to do the same. Perhaps I should completely revise the way I’m tackling the problem?

I would like to hear your opinion: is there a cleaner way to do what I need? Thank you!

---

<div class="post-metadata">

**Author:** ![werpers](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/werpers/32/42610_2.png) [@werpers](https://discourse.julialang.org/u/werpers)\
**Post date:** [September 26, 2024, 11:06am UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906/2 "2024-09-26T11:06:39Z")

</div>

Is it important for you to control the array type?  
If not it seems you could simply do

```julia
map(fn_to_call, list_of_inputs)

```

letting the standard library handle determining the type of array that should be allocated.

If you want more control it might still be possible but I’m guessing the solution then depends on what you’re trying to achieve.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [September 26, 2024, 11:36am UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906/3 "2024-09-26T11:36:01Z")

</div>

I frequently find this kind of situation as well. I’m not sure if there’s a perfectly looking solution. Maybe the one I like most is the one where an initial value can be provided but is computed by default:

```julia-repl
julia> function results2(f, x; init=f(first(x)))
           results_vec = Vector{eltype(init)}(undef, length(x))
           for i in eachindex(x)
               results_vec[i] = f(x[i])
           end
           return results_vec
       end
results3 (generic function with 1 method)

julia> @btime results2($(sin), $(rand(1:10, 10^3)));
  4.071 μs (1 allocation: 7.94 KiB)

```

This does not work well, though, if computing a single value is something very expensive. Then I probably would go with something like you posted or, to make the loop a little more self-contained:

```julia-repl
julia> function results(f, x)
           local results_vec
           for i in eachindex(x)
               r = f(x[i])
               if i == firstindex(x)
                   results_vec = Vector{eltype(r)}(undef, length(x))
               end
               results_vec[i] = r
           end
           return results_vec
       end
results (generic function with 1 method)

julia> @btime results($(sin), $(rand(1:10, 10^3)));
  4.285 μs (1 allocation: 7.94 KiB)

```

ps: I have nothing against the use of `map`, the thing is that I could not find out _how_ it defines the output type, thus one can be unsure if it will be the right choice for the application in question, except for benchmarking. It seems that it _does not_ evaluate the function in advance:

```julia-repl
julia> my_expensive_function(x) = begin sleep(1); sin(x) end
my_expensive_function (generic function with 1 method)

julia> @time map(my_expensive_function, [1,2,3])
  3.006254 seconds (14 allocations: 496 bytes)
3-element Vector{Float64}:
 0.8414709848078965
 0.9092974268256817
 0.1411200080598672

julia> @time results(my_expensive_function, [1,2,3])
  3.005756 seconds (14 allocations: 496 bytes)
3-element Vector{Float64}:
 0.8414709848078965
 0.9092974268256817
 0.1411200080598672

```

```julia

```

---

<div class="post-metadata">

**Author:** ![nilshg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nilshg/32/2283_2.png) [@nilshg](https://discourse.julialang.org/u/nilshg)\
**Post date:** [September 26, 2024, 11:48am UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906/4 "2024-09-26T11:48:04Z")

</div>

What about a list comprehension?

---

<div class="post-metadata">

**Author:** ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)\
**Post date:** [September 26, 2024, 12:09pm UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906/5 "2024-09-26T12:09:42Z")

</div>

I used this function in the past to decide the eltype:

```julia
function infer_eltype(itr)
    T1, T2 = eltype(itr), Base.@default_eltype(itr)
    ifelse(T2 !== Union{} && T2 <: T1, T2, T1)
end

```

it’s not perfect though as can be seen by the discussion in this PR: [Create `infer_eltype` function by Tortar · Pull Request #54909 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/54909) and the associated issue

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [September 26, 2024, 12:38pm UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906/6 "2024-09-26T12:38:05Z")

</div>

> [@nilshg](#):
>
> What about a list comprehension?

The annoying part is that the desired behavior permits users to specify GPU arrays as the destination.

I think there is no “simple julian” single function that:

1. Needs no explicit element/result type from the callsite
2. Robustly avoids unnecessary CPU ↔ GPU transfers (and runs itself on the GPU)

If you are happy with Vector results and on-CPU computation, `map` or list comprehension does the job just fine. This works with any crap `fn_to_call`.

If you need to robustly support GPU, then you cannot feed in any crap `fn_to_call` and your user needs to think about types and whether things reside on device or host memory.

So the best you can do is have two APIs: A simple one, using `map` that allocates a vector, and a more complex one that requiresusers to pre-allocate the destination.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [September 26, 2024, 1:02pm UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906/7 "2024-09-26T13:02:04Z")

</div>

There is internal machinery to collect a generator (akin to a list comprehension) but with support for using `similar` to allocate the array(s) as necessary. It works quite well; it just needs docs and public-ation.

But the “hard part” here — incremental widening — is something that I don’t think you’d be able to do very effectively on a GPU anyway, right? Just look at the first element’s result and generate a `similar` array with the type of that value and go. You can use `Iterators.drop` to skip that first element in the subsequent `for` loop.

---

<div class="post-metadata">

**Author:** ![Maurizio\_Tomasi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maurizio_tomasi/32/384_2.png) [@Maurizio\_Tomasi](https://discourse.julialang.org/u/Maurizio_Tomasi)\
**Post date:** [September 26, 2024, 1:56pm UTC](https://discourse.julialang.org/t/most-elegant-way-to-create-a-vector-of-results-if-the-result-is-an-unknown-type/119906/8 "2024-09-26T13:56:55Z")

</div>

Thank you all for your answers. Yes, I already ruled out `map` and list comprehension as callers might want to use some other data structure than `Array`.

@lmiq It helps me a lot to hear that other people faced similar problems, guess that my approach is not so bad, after all. Thanks for the benchmarks, in fact my case could take some time to compute a single value, so it’s better not to recompute it.

@foobar_lv2, a duplicated API sounds nice. In fact, I reckon that most of the people that will use this code will run it on a CPU, so this would make their life easier.

@mbauman, I’ll take the hint about `Iterators.drop`, thanks!
