# Static arrays and type stability under map

Hello,

I’ve been surprised by the return type of the mapping of a type stable function on a couple of static arrays.

Here is a minimal example, first with regular arrays

``````julia> n = 4;

julia> bool = rand(Bool, n);

julia> pair = [rand(1:n, 2) for i in 1:n];

julia> map(pair) do ci
(&)(bool[ci]...)
end;

julia> typeof(ans)
Array{Bool,1}

``````

Let’s now use static arrays and look at the type of the result:

``````julia> using StaticArrays

julia> sbool = SVector(bool...);

julia> spair = SVector(pair...);

julia> map(spair) do ci
(&)(sbool[ci]...)
end;

julia> typeof(ans)
StaticArrays.SArray{Tuple{4},Any,1,4}

``````

I was surprised to see the element type on return to be `Any` instead of `Bool` in this case.

Is this expected? Or am I missing something? I’ve been scratching my head on this one and would be grateful for any feedback!

V.

What should the code actually do?

Well I would like the code (with static arrays) to return a `StaticArrays.SArray{Tuple{4},Bool,1,4}` (rather than `StaticArrays.SArray{Tuple{4},Any,1,4}`).

The following (adding `::Bool`) works but I wonder why it’s necessary with static arrays, and not with regular arrays…

``````julia> map(spair) do ci
(&)(sbool[ci]...)::Bool
end;

julia> typeof(ans)
StaticArrays.SArray{Tuple{4},Bool,1,4}

``````

My question was what does the `(&)(...)` stuff do, since it is not very comprehensible (to me). Can you give example inputs and outputs? It feels like there should be a better way to do this.

1 Like

Ok sorry my mistake, here is what I’m trying to do… I have a first array that contains booleans (`bool`), and a second with pairs of indices (`pairs`) pointing to the elements of `bool`.

For each pair, the code should test whether both elements are `true`. Here is a slight rewrite that hopefully will make it more understandable.

``````julia> using StaticArrays

julia> n = 10;

julia> m = 5;

julia> sbool = SVector(rand(Bool, n)...);

julia> spair = SVector([rand(1:n, 2) for i in 1:m]...);

julia> map(spair) do ci
sbool[ci] & sbool[ci]
end;

julia> typeof(ans)
StaticArrays.SArray{Tuple{5},Any,1,5}
``````

This needs to be done very fast, which is why I’m insisting on stick to immutable static arrays. Thanks for your help!

`(&)(sbool[ci]...)` can be more idiomatically written as `all(sbool[ci])` but that also results in type instability for `SArray`s.

Looking into StaticArrays.jl, it seems that the same trick (`::Bool` annotation) is used in the package:

If anyone reading this topic has an idea of how the annotation could be alleviated I would be curious to learn more…

1 Like
``````julia> map(spair) do ci
(&)(sbool[ci]...)
end
4-element SArray{Tuple{4},Any,1,4}:
true
false
false
false
``````

To help see what’s going on, I wanted to use `@code_warntype`, which means wrapping it in a function. Suddenly, it’s type stable:

``````julia> foo(spair, sbool) = map(spair) do ci
(&)(sbool[ci]...)
end
foo (generic function with 1 method)

julia> @code_warntype foo(spair, sbool)
Body::SArray{Tuple{4},Bool,1,4}
1 1 ─ %1  = new(getfield(Main, Symbol("##37#38")){SArray{Tuple{4},Bool,1,4}}, %%sbool)::getfield(Main, Symbol("##37#38")){SArray{Tuple{4},Bool,1,4}}       │
│   %2  = :(StaticArrays.Tuple)::Type{Tuple}                                                                                                             │╻╷╷╷  map
│         Core.apply_type(%2, 4)                                                                                                                         ││┃│││  _map
│   %4  = Base.getfield(%%spair, :data)::NTuple{4,Array{Int64,1}}                                                                                        │││╻     macro expansion
│   %5  = Base.getfield(%4, 1, false)::Array{Int64,1}                                                                                                    ││││╻     getindex
│   %6  = invoke %1(%5::Array{Int64,1})::Bool                                                                                                            ││││
│   %7  = Base.getfield(%%spair, :data)::NTuple{4,Array{Int64,1}}                                                                                        │││││╻     getproperty
│   %8  = Base.getfield(%7, 2, false)::Array{Int64,1}                                                                                                    │││││╻     getindex
│   %9  = invoke %1(%8::Array{Int64,1})::Bool                                                                                                            ││││
│   %10 = Base.getfield(%%spair, :data)::NTuple{4,Array{Int64,1}}                                                                                        │││││╻     getproperty
│   %11 = Base.getfield(%10, 3, false)::Array{Int64,1}                                                                                                   │││││╻     getindex
│   %12 = invoke %1(%11::Array{Int64,1})::Bool                                                                                                           ││││
│   %13 = Base.getfield(%%spair, :data)::NTuple{4,Array{Int64,1}}                                                                                        │││││╻     getproperty
│   %14 = Base.getfield(%13, 4, false)::Array{Int64,1}                                                                                                   │││││╻     getindex
│   %15 = invoke %1(%14::Array{Int64,1})::Bool                                                                                                           ││││
│   %16 = StaticArrays.tuple(%6, %9, %12, %15)::NTuple{4,Bool}                                                                                           ││││
│   %17 = new(SArray{Tuple{4},Bool,1,4}, %16)::SArray{Tuple{4},Bool,1,4}                                                                                 ││││╻     Type
└──       goto 3                                                                                                                                         ││││
2 ─       unreachable                                                                                                                                    ││││
3 ─       goto 4                                                                                                                                         ││
4 ─       return %17                                                                                                                                     │

julia> foo(spair, sbool)
4-element SArray{Tuple{4},Bool,1,4}:
true
false
false
false
``````
2 Likes

That did the trick thanks a lot!