# SArray, but heterogeneous?

**URL:** https://discourse.julialang.org/t/sarray-but-heterogeneous/84674
**Category:** General Usage
**Created:** [July 23, 2022, 11:06am UTC](https://discourse.julialang.org/t/sarray-but-heterogeneous/84674 "2022-07-23T11:06:47Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [July 23, 2022, 11:06am UTC](https://discourse.julialang.org/t/sarray-but-heterogeneous/84674/1 "2022-07-23T11:06:47Z")

</div>

Is it possible to have a Julia type for heterogeneous arrays with compile-time-known sizes? In particular I’m thinking of a heterogeneous matrix, so it would be parameterized by number of rows, number of columns and with a type for each of its elements. The latter part is problematic.

Wait a minute I think I got an idea!

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [July 23, 2022, 11:30am UTC](https://discourse.julialang.org/t/sarray-but-heterogeneous/84674/2 "2022-07-23T11:30:03Z")

</div>

I _think_ this is feasible. You can wrap a `Tuple` like the other StaticArrays do, except your array would be fully parametrized by the `Tupl`e’s type, which contains the types of each element. It’ll be odd because an `AbstractArray` should have a singular `eltype` aka `T`, but maybe all of your arrays could just use `Any`. And if you subtype `StaticArray` too, you’ll have to adhere to more type parameter conventions.

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [July 23, 2022, 11:31am UTC](https://discourse.julialang.org/t/sarray-but-heterogeneous/84674/3 "2022-07-23T11:31:01Z")

</div>

I think it’s going to be something like this:

```julia
struct HSArray{T <: Tuple, size}
  data::T

  function HSArray(t::T, ::Val{size}) where {T <: Tuple, size}
    (length(t) == prod(size)) || error("sizing error")
    new{T, size}(t)
  end
end

Base.length(::HSArray{<: Tuple, size}) where {size} = prod(size)
Base.size(::HSArray{<: Tuple, size}) where {size} = size

Base.IndexStyle(::HSArray{<: Tuple, size}) where {size} = Base.IndexLinear()

```

Just need to implement some more interfaces from [Interfaces · The Julia Language](https://docs.julialang.org/en/v1.8.0-rc3/manual/interfaces/)

Actually, those interfaces would be of limited use for my type because `eltype(::HSarray)` would need to be `Any`, as Benny said.

The most useful method for me would be `getindex`, which seems easy to implement.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [July 23, 2022, 11:50am UTC](https://discourse.julialang.org/t/sarray-but-heterogeneous/84674/4 "2022-07-23T11:50:37Z")

</div>

Now that I check the Interface docs, the `eltype` is said to be important, but I’m not familiar with how exactly, except for allocating methods like `collect` and the default `similar`. So I don’t know how using `Any` all the time would pan out. I wondered if the `eltype` affects the type stability of critical methods like `getindex`, but it seems like you could just forward it to the wrapped `Tuple` ~~and it should be inferred~~ , which is technically type-unstable but the compiler does infer the output type given a constant index. `setindex!` is obviously harder for a wrapped `Tuple`, but if you intend to implement that, you could look into `MArray`’s “pointer manipulation”.

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [July 23, 2022, 12:02pm UTC](https://discourse.julialang.org/t/sarray-but-heterogeneous/84674/5 "2022-07-23T12:02:08Z")

</div>

For my usecase I don’t think I need `setindex!`, or iteration, and if I don’t need iteration then `eltype` shouldn’t matter either, I think. So this is easy, unlike what I thought at first.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [July 23, 2022, 12:18pm UTC](https://discourse.julialang.org/t/sarray-but-heterogeneous/84674/6 "2022-07-23T12:18:45Z")

</div>

The default `iterate` for `AbstractArray`s is based on scalar `getindex`, so you’ll get it for free.

---

<div class="post-metadata">

### Author: ![circonflexe](https://avatars.discourse-cdn.com/v4/letter/c/c57346/32.png) [@circonflexe](https://discourse.julialang.org/u/circonflexe)
#### Post date: [May 3, 2025, 3:21pm UTC](https://discourse.julialang.org/t/sarray-but-heterogeneous/84674/7 "2025-05-03T15:21:12Z")

</div>

Just for info and in case this interests someone, I recently wrote this in some of my code:

```julia
struct HeterogeneousStaticArray{S<:Tuple,D,T,X<:Tuple} <: StaticArray{S,T,D}
    data::X
  end
  # HeterogeneousStaticArray{S,D,T}(x::X) where{S,D,T,X} =
  # HeterogeneousStaticArray{S,D,T,X}(x)
  const HSVector{N,T} =
    HeterogeneousStaticArray{Tuple{N},1,T}
  const HSMatrix{N1,N2,T} =
    HeterogeneousStaticArray{Tuple{N1,N2},2,T}
  
  @propagate_inbounds Base.getindex(a::HeterogeneousStaticArray, i::Int) = a.data[i]
  # these two lines are copied from LinearAlgebra/adjtrans.jl,
  # only removing the typeassert for the result:
  @propagate_inbounds Base.getindex(v::LinearAlgebra.AdjOrTrans{T,<:HSVector},
    i::Int) where {T} =
    LinearAlgebra.wrapperop(v)(v.parent[i-1+first(axes(v.parent)[1])])
  @propagate_inbounds Base.getindex(A::LinearAlgebra.AdjOrTrans{T,<:HSMatrix},
    i::Int, j::Int) where {T} = LinearAlgebra.wrapperop(A)(A.parent[j, i])
  
  """ HSA[x1 x2;x3 x4]
  
  A constructor building a static array with heterogeneous values
  (stored in a tuple)."""
  struct HSA{T} end
  Base.getindex(h::Type{HSA}, x...) = similar_type(h, Size(length(x)))(x)
  
  Base.typed_hvcat(h::Type{HSA}, rows::Dims, x...) =
    _typed_hvcat_tr(h, rows, x)
  Base.typed_hvcat(h::Type{HSA}, rows::Dims, x::Number...) = # disambiguation
    _typed_hvcat_tr(h, rows, x)
  function _typed_hvcat_tr(h, rows, x)
    s = StaticArrays._SA_hvcat_transposed_size(rows)
    isnothing(s) && throw(ArgumentError(string(
      "HSA[...] matrix with inconsistent row lengths: ", rows)))
    similar_type(h, s)(StaticArrays.reorder(x, Val(s[1]), Val(s[2])))
  end
(H::Type{HeterogeneousStaticArray{S}})(x::Tuple) where{S} =
    StaticArrays.construct_type(H, x)(x)
  StaticArrays.similar_type(::Type{HSA}, ::Size{S}) where{S} =
    HeterogeneousStaticArray{Tuple{S...}}
  StaticArrays.construct_type(::Type{H}, x) where{H<:HeterogeneousStaticArray} =
    StaticArrays.adapt_eltype(StaticArrays.adapt_size(H, x), x)
  function StaticArrays.adapt_eltype(::Type{HeterogeneousStaticArray{S,D}},
      x) where{S,D}
    T = StaticArrays.promote_tuple_eltype(x)
    return HeterogeneousStaticArray{S,D,T,typeof(x)}
  end

```
