# Method matching an array with elements of a composite type with parametric types

Here is my composite type:

``````mutable struct Factor{T, N}
vars::NTuple{N,Int64}
vals::Array{T,N}
end
``````

How can I define a method that matches arrays with elements of this type and that can access the parametric type `T`?

Here is my attempt:

``````function product(F::Array{Factor{T,N},1}) where {T,N}
reduce(product, F; init = Factor{T}((), Array{T,0}(undef)))
end
``````

But it fails:

``````A = Factor{Float64,1}((1,), [0.11; 0.89])
B = Factor{Float64,2}((2, 1), [0.59 0.22; 0.41 0.78])
C = product([A, B])
``````

Error:

``````MethodError: no method matching product(::Array{Factor{Float64,N} where N,1})
``````

The signature `product(F::Array{Factor{T,N},1}) where {T,N}` requires that `N` be a defined value for the whole (specialized) method. But in your case you want to allow different `N` values for different array elements, so you can write:

``````function product(F::Array{Factor{T,N} where N, 1}) where T
# ... code that uses `T`
end
``````

or equivalently, just leave `N` unspecified:

``````function product(F::Array{Factor{T}, 1}) where T
# ... code that uses `T`
end
``````

However, you need to change the body of the method to choose a specific `N` when you instantiate a `Factor`.

1 Like

Thank you very much (also for anticipating the next bug in my program)

1 Like

This will only work when the user has specifically created an `Array{Factor{T}, 1}`, that is, an array in which every different element can have a potentially different `N` in its `Factor` parameters. That’s almost certainly not what you want, because it will fail in some simple cases:

``````julia> product([A])
ERROR: MethodError: no method matching product(::Array{Factor{Float64,1},1})
You may have intended to import Base.product
Closest candidates are:
product(::Array{Factor{T,N} where N,1}) where T at REPL:1
Stacktrace:
 top-level scope at REPL:100:
``````

You almost certainly want:

``````function product(F::Array{<:Factor{T}, 1}) where {T}
# code that uses `T`
end
``````

which accepts any array whose element type is some subtype of `Factor{T}`.

Even better, unless you actually care about only accepting the built-in type `Array` and not any other array-like object, you probably want:

``````function product(F::AbstractArray{<:Factor{T}, 1}) where {T}
``````

or, exactly equivalently:

``````function product(F::AbstractVector{<:Factor{T}}) where {T}
``````

since `AbstractVector{T}` is exactly `AbstractArray{T, 1}`.

2 Likes

Or maybe even

``````function product(F::AbstractArray{<:Factor{T}}) where {T}
``````

?

1 Like

Could you explain why

``````product(F::Array{Factor{T}, 1}) where T
``````

fails in simple cases like the one you show?

You mention that:

This will only work when the user has specifically created an `Array{Factor{T}, 1}` , that is, an array in which every different element can have a potentially different `N` in its `Factor` parameters.

Isn’t

``````julia> product([A])
``````

Covered by that statement?

In which other simple cases does it fail?

In particular, `Array{Factor{T}, 1}` and `Array{Factor{T, 1}, 1}` are totally different types, but they are both subtypes of `Array{<:Factor{T}, 1}`

2 Likes

What change would I need to make to

``````function product(F::Array{Factor{T,N} where N, 1}) where T
# ... code that uses `T`
end
``````

to be able to access `N` inside the body of the function?

You can’t… That would require that `N` is a defined value for the whole method, which is not the case.

Say the method is called like this:

``````A = Factor{Float64,1}((1,), [0.11; 0.89])
B = Factor{Float64,2}((2, 1), [0.59 0.22; 0.41 0.78])
C = product([A, B])
``````

Here `N=1` for `A`, and `N=2` for `B`. So what should `N` be in the body of `product`?

1 Like