Tuples of "any container" of a parametric type - give me the parameter

I wish to write a function foo, which takes, among arguments, a Tuple. Each element in the Tuple can be a scalar or an Array of arbitrary dimension. The scalars and array elements are all of the same type Mytype{Nmy} (Nmy shall be common for all elements in the Tuple).

My function will return a scalar or an Array of scalars, where the scalars are of type Mytype{Nmy}

I need to know N, so that I can write typestable code

using     StaticArrays

struct Mytype{N}
    x  :: Float64
    dx :: SVector{N,Float64}
Mytype(x,dx::Vector{Float64})  =Mytype(x,SVector{size(dx,1),Float64}(dx))
Base.:(+)(a::Mytype{N},b::Mytype{N}) where {N} = Mytype{N}(a.x+b.x,a.dx+b.dx)

MytypeContainer{N} = Union{Mytype{N},Array{Mytype{N}}}

function foo(args::NTuple{nx,MytypeContainer{N}}) where {N,nx}
    s = Mytype{N}(0.,zeros(N))
    for arg in args 
        if isa(arg,Mytype{N})
            s += arg
            for x in arg
                s += x
    return s

v1 = Mytype{2}(3.,[1.,2.])
v2 = Mytype{2}(4.,[5.,6.])
v = [v1,v2]
@show foo((v,))
@show foo((v1,))
@show foo((v1,v2))
@show foo((v,v))
@show foo((v1,v))

In ways I do not understand,


is not expressing what I intend: a tuple of any type of containers of Mytype{N}, and give me N for typestable allocation.

P.S. The code above is now updated and seems to pass the test.

It is not entirely clear what you want, but

  1. Mytype(0.,zeros(N)) is not type stable to start with,
  2. use @code_warntype to investigate type stability,
  3. why are you wrapping a single argument in a Tuple? it should be inferrable, but hard to see the reason for it.


Mytype(x,dx::NTuple{N,Float64}) where {N}

instead. And use

s = Mytype{N}(0.,ntuple((i)->0., Val{N}))

to initialize in a type-stable way.

Also give a minimum working example (MWE) to make it easier to help you.

1 Like

Tamas, Mohamed,

I see from your answers that I have been very poor at explaining myself! Apologies and thank your for your effort.

I have provided some code above, intended as a MWE. Note that I have updated it since my first post. At present, it works (as in: it compiles, and gives all the expected answers). Whether the code is typestable is an open question, I am still learning the ropes there.


@code_warntype foo((v,))

gives me


      s::Mytype{2} = $(Expr(:invoke, MethodInstance for Mytype{2}(::Float64, ::Array{Float64,1}), Mytype{2}, 0.0, :($(Expr(:invoke, MethodInstance for fill!(::Array{Float64,1}, ::Float64), :(Base.fill!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Float64,1}, svec(Any, Int64), Array{Float64,1}, 0, :($(Expr(:static_parameter, 1))), 0))), :((Base.sitofp)(Float64, 0)::Float64)))))) # line 14:
      #temp#@_6::Int64 = 1
      unless (Base.not_int)((Base.slt_int)(1, #temp#@_6::Int64)::Bool)::Bool goto 28
      SSAValue(5) = (Base.getfield)(args::Tuple{Array{Mytype{2},1}}, #temp#@_6::Int64)::Array{Mytype{2},1}
      SSAValue(6) = (Base.add_int)(#temp#@_6::Int64, 1)::Int64
      arg::Array{Mytype{2},1} = SSAValue(5)
      #temp#@_6::Int64 = SSAValue(6) # line 15:
      goto 13 # line 16:
      13:  # line 18:
      #temp#@_4::Int64 = 1
      unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)((Base.arraylen)(arg::Array{Mytype{2},1})::Int64, 1)::Int64)::Bool)::Bool goto 26
      SSAValue(7) = (Base.arrayref)(arg::Array{Mytype{2},1}, #temp#@_4::Int64)::Mytype{2}
      SSAValue(8) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64
      x::Mytype{2} = SSAValue(7)
      #temp#@_4::Int64 = SSAValue(8) # line 19:
      s::Mytype{2} = $(Expr(:invoke, MethodInstance for +(::Mytype{2}, ::Mytype{2}), :(Main.+), :(s), :(x)))
      goto 16
      goto 4
      28:  # line 23:
      return s::Mytype{2}

which could be bad news or good news!

Please give some code I can copy and paste to REPL :slight_smile:

Edit: oh it’s above, sorry didn’t see it.

You are not calling the above function at all by running Mytype{2}(3.,[1.,2.]). Instead, you are calling the inner constructor, that is you are telling Julia that you want an instance of the concrete type MyType{2} thus N is now known to the compiler, and a conversion of the second argument happens behind the scene from the array you are passing to SVector{2,Float64} as N is 2 in your example. Your code therefore seems type stable because you are avoiding this type unstable function.