# Reduce + hcat is type unstable

**URL:** <https://discourse.julialang.org/t/reduce-hcat-is-type-unstable/112800>\
**Category:** General Usage\
**Created:** [April 10, 2024, 8:07pm UTC](https://discourse.julialang.org/t/reduce-hcat-is-type-unstable/112800 "2024-04-10T20:07:27Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![mtanneau](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mtanneau/32/17787_2.png) [@mtanneau](https://discourse.julialang.org/u/mtanneau)\
**Post date:** [April 10, 2024, 8:07pm UTC](https://discourse.julialang.org/t/reduce-hcat-is-type-unstable/112800/1 "2024-04-10T20:07:27Z")

</div>

I have observed the following type instability when using reduce + hcat as follows (see examples below). Is this intended? Could it be related to [issue#46331](https://github.com/JuliaLang/julia/issues/46331)?

More context: I am using this syntax, as recommended [here](https://discourse.julialang.org/t/using-hcat-with-splatting-on-arrays-of-different-lengths-kills-julia/15733/2)., when converting a Vector of scalar or arrays into a higher-dimensional array (e.g., a 128-long vector of 2x3-sized matrices is converted into a 2x3x128 array).

```julia
julia> reduce(hcat, ones(4))
1×4 Matrix{Float64}:
 1.0 1.0 1.0 1.0

julia> reduce(hcat, ones(1))
1.0

julia> @code_warntype reduce(hcat, ones(4))
MethodInstance for reduce(::typeof(hcat), ::Vector{Float64})
  from reduce(op, A::AbstractArray; kw...) @ Base reducedim.jl:406
Arguments
  #self#::Core.Const(reduce)
  op::Core.Const(hcat)
  A::Vector{Float64}
Body::Union{Float64, Matrix{Float64}}
1 ─ %1 = Core.NamedTuple()::Core.Const(NamedTuple())
│ %2 = Base.pairs(%1)::Core.Const(Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}())
│ %3 = Base.:(var"#reduce#802")(%2, #self#, op, A)::Union{Float64, Matrix{Float64}}
└── return %3

```

Namely, it seems that `reduce(hcat, v)` returns a `Matrix` if `v` has more than 1 element, but a scalar if `v` has one element.

Note that, in contrast, using `hcat` + splatting yields a consistent behavior

```julia
julia> hcat(ones(4)...)
1×4 Matrix{Float64}:
 1.0 1.0 1.0 1.0

julia> hcat(ones(1)...)
1×1 Matrix{Float64}:
 1.0

julia> @code_warntype hcat(ones(1)...)
MethodInstance for hcat(::Float64)
  from hcat(X::T...) where T<:Number @ Base abstractarray.jl:1609
Static Parameters
  T = Float64
Arguments
  #self#::Core.Const(hcat)
  X::Tuple{Float64}
Locals
  @_3::Union{Nothing, Tuple{Int64, Int64}}
  @_4::Int64
  @_5::Union{Nothing, Tuple{Int64, Int64}}
  j::Int64
  i::Int64
Body::Matrix{Float64}
# 
# skipping rest of output
# 

```

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [April 10, 2024, 8:23pm UTC](https://discourse.julialang.org/t/reduce-hcat-is-type-unstable/112800/2 "2024-04-10T20:23:48Z")

</div>

I think you want `stack`.

The earlier magic methods of `reduce(hcat, xs)` are quite specific, and your example isn’t hitting them. It is just doing `hcat(1.0, 1.0)`, `hcat(hcat(1.0, 1.0), 1.0)` and so on. Combining elements zero times returns just `1.0`.

```julia
julia> @which reduce(hcat, ones(4))
reduce(op, A::AbstractArray; kw...)
     @ Base reducedim.jl:406

julia> @which reduce(hcat, eachcol(ones(3,4)))
reduce(::typeof(hcat), A::AbstractVector{<:AbstractVecOrMat})
     @ Base abstractarray.jl:1721

julia> reduce(hcat, ones(1))
1.0

julia> reduce(hcat, ones(2))
1×2 Matrix{Float64}:
 1.0 1.0

```

---

<div class="post-metadata">

**Author:** ![mtanneau](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mtanneau/32/17787_2.png) [@mtanneau](https://discourse.julialang.org/u/mtanneau)\
**Post date:** [April 10, 2024, 8:59pm UTC](https://discourse.julialang.org/t/reduce-hcat-is-type-unstable/112800/3 "2024-04-10T20:59:23Z")

</div>

Thanks! I just independently found out about `stack` indeed 🙂

---

<div class="post-metadata">

**Author:** ![mtanneau](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mtanneau/32/17787_2.png) [@mtanneau](https://discourse.julialang.org/u/mtanneau)\
**Post date:** [April 10, 2024, 9:44pm UTC](https://discourse.julialang.org/t/reduce-hcat-is-type-unstable/112800/4 "2024-04-10T21:44:33Z")

</div>

Quick note: `stack` does not seem to work well with `Vector{String}` input:

```julia
julia> stack(["hello", "world!"])
ERROR: MethodError: no method matching size(::String)

Stacktrace:
 [1] axes(A::String)
   @ Base ./abstractarray.jl:98
 [2] _stack_size_check
   @ ./abstractarray.jl:2870 [inlined]
 [3] _typed_stack(::Colon, ::Type{Char}, ::Type{String}, A::Vector{String}, Aax::Tuple{Base.OneTo{Int64}})
   @ Base ./abstractarray.jl:2802
 [4] _typed_stack
   @ ./abstractarray.jl:2793 [inlined]
 [5] _stack
   @ ./abstractarray.jl:2783 [inlined]
 [6] _stack
   @ ./abstractarray.jl:2775 [inlined]
 [7] #stack#178
   @ ./abstractarray.jl:2743 [inlined]
 [8] stack(iter::Vector{String})
   @ Base ./abstractarray.jl:2743
 [9] top-level scope
   @ REPL[7]:1

```

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [April 11, 2024, 12:29am UTC](https://discourse.julialang.org/t/reduce-hcat-is-type-unstable/112800/5 "2024-04-11T00:29:32Z")

</div>

That’s a bug, I think, but a bug in the error-printing function. `stack` claims to accept any iterator of iterators, but all the inner ones must have the same size.

```julia
julia> stack(["hello", "world"])
5×2 Matrix{Char}:
 'h' 'w'
 'e' 'o'
 'l' 'r'
 'l' 'l'
 'o' 'd'

julia> stack(["hello", "world!"])
ERROR: MethodError: no method matching size(::String)

julia> stack(["hello", "world!"] .|> collect)
ERROR: DimensionMismatch: stack expects uniform slices, got axes(x) == (1:6,) while first had (1:5,)
Stacktrace:
 [1] _stack_size_check
   @ ./abstractarray.jl:2895 [inlined]

julia> @eval Base @inline function _stack_size_check(x, ax1::Tuple)
           if _iterator_axes(x) != ax1
               uax1 = map(UnitRange, ax1)
               uaxN = map(UnitRange, _iterator_axes(x)) # this line had axes(x)
               throw(DimensionMismatch(
                   LazyString("stack expects uniform slices, got axes(x) == ", uaxN, " while first had ", uax1)))
           end
       end
_stack_size_check (generic function with 1 method)

julia> stack(["hello", "world!"])
ERROR: DimensionMismatch: stack expects uniform slices, got axes(x) == (1:6,) while first had (1:5,)

```
