Interfaces with multiple functions

Most interfaces are larger than just a single function, requiring multiple methods to be implemented in order to conform to the interface. One example for this is AbstractArray, which at minimum (for linear indexing) requires these methods to be implemented:

  • size(::AbstractArray)
  • getindex(::AbstractArray, i::Int)

This interface can be described like so:

using RequiredInterfaces
const RI = RequiredInterfaces

abstract type LinearArray{T,N} <: AbstractArray{T,N} end
@required LinearArray begin
    Base.size(::LinearArray)
    Base.getindex(::LinearArray, ::Int)
end
getindex (generic function with 193 methods)

Importantly, we don't need to subtype LinearArray in order to check whether a type would implement the interface:

julia> using Test
julia> @test RI.check_interface_implemented(LinearArray, Vector)Test Passed

At the moment, this doesn't handle fallback definitions of abstract types well - for example, if we do the same check with Array, which has a fallback size defined:

julia> using Test
julia> @test RI.check_interface_implemented(LinearArray, Array)Test Passed

While unfortunate, this is only a limitation of the current implementation, and should be remedied in the future.

Another limitation is that we can't use LinearArray to dispatch Array objects, due to Julia not recognizing that the concrete instantiations of Array (Vector etc.) do implement the interface correctly - this is something that could be remedied with the solution presented in About Interfaces, though there are multiple other venues as well.

For now, checks like these can serve as information on whether a type does conform to the interface correctly, even if it doesn't formally subtype the abstract type behind this interface.