Promote parametric type in repeat

Just for laziness I would like to implement a `repeat` function for my own parametric type. For example, consider the following example, where `A1` might even be used recursively (i.e. Arrays of Arrays) and I would like to do a simple repeat (introducing one further dimension, here a second one)

``````import Base: size, repeat
abstract type A end

struct A1{C<:Array{<:A,N} where N} <: A
value::C
end
struct A2{T} <: A
value::T
end

size(a::A1) = size(a.value)
repeat(a::A1; inner=nothing, outer=nothing) = A1( repeat(a.value; inner=inner, outer=outer) )
repeat(a::A1, counts...) = A1( repeat(a.value, counts...) )

function runExample()
a = A1([ A2(0.2); A2(0.2)])
s = length(size(a))
d = fill(1,s+1)
d[s+1] = s
return repeat(a; inner=d)
end
@code_warntype runExample()
runExample()
``````

While the example runs perfectly (last line) the `@code_warntype` says that it can’t infer the type of the repeat return, i.e.

``````   11 ─ %25 = invoke Main.:(#repeat#8)(%7::Array{Int64,1}, %10::Nothing, %9::Function, %4::A1{Array{A2{Float64},1}})::A1{_1} where _1
``````

I started trying something like

``````repeat(a::A1{Ca}; inner=nothing, outer=nothing) where {Ca <: Array{C,N} where {C <: A, N}} = ...
``````

however the new type will have a different `N`, since the array dimensions change, so I don’t know how to continue. Actually the problem is similar for `cat`, `vcat` and `hcat`.

How can I properly define `repeat` to not avoid the type instability?

I just noticed that it is enough to specify one of the parameters, i.e.

``````repeat(a::A1{C}; inner=nothing, outer=nothing) where {C <:Array{P,N}} where {P<:A,N} = A1{Array{P}}( repeat(a.value; inner=inner, outer=outer) )
repeat(a::A1{C}, counts...)  where {C <:Array{P,N}} where {P<:A,N} = A1{Array{P}}( repeat(a.value, counts...) )
``````

note that for the resulting type `A1{Array{P}}` the `N` is not specified. This resolves the `@code_warntype` problem partly, but there still seems to be a problem determining the resulting number dimensions.

Interestingly, at least when I try to compute the dimensions of the resulting array (the lazy way), i.e. the script looks like

``````import Base: size, repeat
abstract type A end

struct A1{C<:Array{<:A,N} where N} <: A
value::C
end
struct A2{T} <: A
value::T
end

size(a::A1) = size(a.value)
function repeat(a::A1{Array{P,N}}; inner=nothing, outer=nothing) where {P<:A,N}
b = repeat(a.value; inner=inner, outer=outer)
return A1{Array{P,ndims(b)}}(b)
end
function repeat(a::A1{Array{P,N}}, counts...) where {P<:A,N}
b = repeat(a.value, counts...)
return A1(b)
end
``````

Then comparing defining a function with the code running in REPL like

``````function runExample()
a = A1([ A2(0.2); A2(0.2)])
s = length(size(a))
d = fill(1,s+1)
d[s+1] = s
return repeat(a, d...)
end
@code_warntype runExample()
runExample()
``````

vs.

``````a = A1([ A2(0.2); A2(0.2)])
s = length(size(a))
d = fill(1,s+1)
d[s+1] = s
@code_warntype repeat(a, d...)
``````

yields that the function can not infer its return type, i.e. I get (just the last part of code_warntype)

``````   │   %7  = invoke Base.fill!(%6::Array{Int64,1}, 1::Int64)::Array{Int64,1}                                   ││┃    fill
25 │         (Base.arrayset)(true, %7, 1, 2)                                                                   │╻    setindex!
26 │   %9  = (Core.tuple)(%4)::Tuple{A1{Array{A2{Float64},1}}}                                                 │
│   %10 = (Core._apply)(Main.repeat, %9, %7)::A1{_1} where _1                                               │
└──       return %10
``````

versus the last lines from the second code_warntype yield

``````   │   %4 = invoke Main.repeat(%1::Array{A2{Float64},1}, %2::Int64, %3::Int64)::Array{A2{Float64},2}           │
19 │   %5 = %new(A1{Array{A2{Float64},2}}, %4)::A1{Array{A2{Float64},2}}                                       │╻╷ Type
└──      return %5
``````

So, interestingly, the first does know the types of `%7` and `%9` but does not use them in the `apply`, while in REPL in `invoke`s the repeat and can pass down the types. Can somebody explain (and maybe solve) this difference?