How to define a function that accepts a vector when all elements are <: S even if the vector is defined as Vector{T} with S <: T

I have a situation where I want to remain general on the type of a specific container (AbstractAbstractFoo), as in the future I may develop other types (say AbstractFoo2 in the example below), but I have a concrete application for a specific subtype (AbstractFoo).

The problem is that when my container is passed to a function defined over the specific type, even if all the elements are of that specific type, of course, it gives a MethodError.

How do you deal with situations like this one ? Manual checking of all the elements of the vector?
Calling identity.(x) before bla ? Is this the right function to call to get a container type to the “minimum common denominator” of the element types ?

abstract type AbstractAbstractFoo end
abstract type AbstractFoo <: AbstractAbstractFoo end

mutable struct Foo <: AbstractFoo end

x = AbstractAbstractFoo[]


bla(x::Vector{T}) where {T<:AbstractFoo}  = println("hello")

bla(x) # MethodError

I do not think you can do that. Your vector can in-principle store entities that are not AbstractFoo, so it makes sense to me that your bla gives a method error. Basically, you are asking for a method that dispatches not on the type of a container (something immutable on which you can efficiently dispatch), rather you are asking for it to dispatch on the content (which would be very inefficient as you have to check the entire content every time you run the method).

You can define bla_elem for each type of element and have a bla that takes a container (a Vector{T} where {T}) and calls bla_elem` fo each element (considering the type of the element does not change how the collection is handled only its individual elements). This will not be efficient but will work. You can define specializations for specific vector specializations, but as you perceived, what matters for dispatch is the type of the container (not its actual values).

I am now using x = identity.(x) before the bla call. Is this the best way to change the type of a container to the most specific type whose all members are subtypes ?

This is probably the easiest way, but I do not know if Julia gives you any guarantees about using the most specific type in the container returned by broadcast. The documentation for broadcast does not specify it, but maybe you can implement your own promotion rules to guarantee that.

1 Like

YASGuide says don’t use dispatch for this:

  • Dispatching on type parameters should be avoided unless necessary, especially if the method author does not “own” the method and/or the type being parameterized (in which case, this kind of dispatch could be an instance of “type piracy”). This is because naive dispatch on type parameters often does not (and cannot) describe values the way the programmer intends, unless the programmer explicitly accounts for invariance. For example, AbstractArray{<:MyType} does not describe “any value of type <:AbstractArray containing elements of type <:MyType”; it only describes a subset of such values (e.g. typeof(Any[1]) <: AbstractArray{<:Number} is false). Likewise, AbstractArray{>:MyType} describes “any value of type <:AbstractArray that could contain elements of type <:MyType”, which is likely a superset of what the programmer intended to describe (e.g. AbstractArray{Any} <: AbstractArray{>:Number} is true). This guideline can generally be relaxed when the method author “owns” both the method and the type being parameterized.