DerivableInterfaces.jl
About
This is a package for defining and deriving functionality for objects based around interfaces and traits, outside of the Julia type hierarchy. It is heavily inspired by Moshi.@derive, which itself is inspired by Rust's derive functionality, and the design of ArrayLayouts.jl. See also ForwardMethods.jl.
The basic idea is to define implementations of a set of functions for a given interface, and types can overload, or derive, those implementations by specifying the desired interface. This provides a systematic way to define implementations of functions for objects that act in a certain way but may not share convenient abstact supertypes, such as a sparse array object and wrappers around that sparse array. Like in Moshi.jl and Rust's derive functionality, traits are simply sets of functions that can be derived for a certain type.
Support

DerivableInterfaces.jl is supported by the Flatiron Institute, a division of the Simons Foundation.
Installation instructions
This package resides in the ITensor/ITensorRegistry local registry. In order to install, simply add that registry through your package manager. This step is only required once.
julia> using Pkg: Pkg
julia> Pkg.Registry.add(url="https://github.com/ITensor/ITensorRegistry")or:
julia> Pkg.Registry.add(url="git@github.com:ITensor/ITensorRegistry.git")if you want to use SSH credentials, which can make it so you don't have to enter your Github ursername and password when registering packages.
Then, the package can be added as usual through the package manager:
julia> Pkg.add("DerivableInterfaces")Examples
using DerivableInterfaces:
DerivableInterfaces, @array_aliases, @derive, @interface, interface
using Test: @testDefine an interface.
struct SparseArrayInterface endDefine interface functions.
@interface ::SparseArrayInterface function Base.getindex(a, I::Int...)
checkbounds(a, I...)
!isstored(a, I...) && return getunstoredindex(a, I...)
return getstoredindex(a, I...)
end
@interface ::SparseArrayInterface function Base.setindex!(a, value, I::Int...)
checkbounds(a, I...)
iszero(value) && return a
if !isstored(a, I...)
setunstoredindex!(a, value, I...)
return a
end
setstoredindex!(a, value, I...)
return a
endcall (generic function with 38 methods)Define a type that will derive the interface.
struct SparseArrayDOK{T,N} <: AbstractArray{T,N}
storage::Dict{CartesianIndex{N},T}
size::NTuple{N,Int}
end
storage(a::SparseArrayDOK) = a.storage
Base.size(a::SparseArrayDOK) = a.size
function SparseArrayDOK{T}(size::Int...) where {T}
N = length(size)
return SparseArrayDOK{T,N}(Dict{CartesianIndex{N},T}(), size)
end
function isstored(a::SparseArrayDOK, I::Int...)
return CartesianIndex(I) in keys(storage(a))
end
function getstoredindex(a::SparseArrayDOK, I::Int...)
return storage(a)[CartesianIndex(I)]
end
function getunstoredindex(a::SparseArrayDOK, I::Int...)
return zero(eltype(a))
end
function setstoredindex!(a::SparseArrayDOK, value, I::Int...)
storage(a)[CartesianIndex(I)] = value
return a
end
function setunstoredindex!(a::SparseArrayDOK, value, I::Int...)
storage(a)[CartesianIndex(I)] = value
return a
endsetunstoredindex! (generic function with 1 method)Specify the interface the type adheres to.
DerivableInterfaces.interface(::Type{<:SparseArrayDOK}) = SparseArrayInterface()Define aliases like SparseMatrixDOK, AnySparseArrayDOK, etc.
@array_aliases SparseArrayDOKUnion{Main.SparseArrayDOK{T, 1}, Main.SparseArrayDOK{T, 2}, Base.LogicalIndex{T, <:Main.SparseArrayDOK}, Base.ReinterpretArray{T, 1, <:Any, <:Union{SubArray{<:Any, <:Any, var"#s19"}, var"#s19"}} where var"#s19"<:Main.SparseArrayDOK, Base.ReinterpretArray{T, 2, <:Any, <:Union{SubArray{<:Any, <:Any, var"#s19"}, var"#s19"}} where var"#s19"<:Main.SparseArrayDOK, Base.ReshapedArray{T, 1, <:Union{Base.ReinterpretArray{<:Any, <:Any, <:Any, <:Union{SubArray{<:Any, <:Any, var"#s20"}, var"#s20"}}, SubArray{<:Any, <:Any, var"#s20"}, var"#s20"}} where var"#s20"<:Main.SparseArrayDOK, Base.ReshapedArray{T, 2, <:Union{Base.ReinterpretArray{<:Any, <:Any, <:Any, <:Union{SubArray{<:Any, <:Any, var"#s20"}, var"#s20"}}, SubArray{<:Any, <:Any, var"#s20"}, var"#s20"}} where var"#s20"<:Main.SparseArrayDOK, SubArray{T, 1, <:Union{Base.ReinterpretArray{<:Any, <:Any, <:Any, <:Union{SubArray{<:Any, <:Any, var"#s21"}, var"#s21"}}, Base.ReshapedArray{<:Any, <:Any, <:Union{Base.ReinterpretArray{<:Any, <:Any, <:Any, <:Union{SubArray{<:Any, <:Any, var"#s21"}, var"#s21"}}, SubArray{<:Any, <:Any, var"#s21"}, var"#s21"}}, var"#s21"}} where var"#s21"<:Main.SparseArrayDOK, SubArray{T, 2, <:Union{Base.ReinterpretArray{<:Any, <:Any, <:Any, <:Union{SubArray{<:Any, <:Any, var"#s21"}, var"#s21"}}, Base.ReshapedArray{<:Any, <:Any, <:Union{Base.ReinterpretArray{<:Any, <:Any, <:Any, <:Union{SubArray{<:Any, <:Any, var"#s21"}, var"#s21"}}, SubArray{<:Any, <:Any, var"#s21"}, var"#s21"}}, var"#s21"}} where var"#s21"<:Main.SparseArrayDOK, LinearAlgebra.Adjoint{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.Adjoint{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.Bidiagonal{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.Bidiagonal{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.Diagonal{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.Diagonal{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.Hermitian{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.Hermitian{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.LowerTriangular{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.LowerTriangular{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.Symmetric{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.Symmetric{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.Transpose{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.Transpose{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.Tridiagonal{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.Tridiagonal{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.UnitLowerTriangular{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.UnitLowerTriangular{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.UnitUpperTriangular{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.UnitUpperTriangular{T, <:Main.SparseArrayDOK{T, 2}}, LinearAlgebra.UpperTriangular{T, <:Main.SparseArrayDOK{T, 1}}, LinearAlgebra.UpperTriangular{T, <:Main.SparseArrayDOK{T, 2}}, PermutedDimsArray{T, 1, <:Any, <:Any, <:Main.SparseArrayDOK}, PermutedDimsArray{T, 2, <:Any, <:Any, <:Main.SparseArrayDOK}} where TDerivableInterfaces the interface for the type.
@derive (T=SparseArrayDOK,) begin
Base.getindex(::T, ::Int...)
Base.setindex!(::T, ::Any, ::Int...)
end
a = SparseArrayDOK{Float64}(2, 2)
a[1, 1] = 2
@test a[1, 1] == 2
@test a[2, 1] == 0
@test a[1, 2] == 0
@test a[2, 2] == 0
@test a isa SparseMatrixDOK
@test a' isa AnySparseMatrixDOKTest PassedCall the sparse array interface on a dense array.
isstored(a::AbstractArray, I::Int...) = true
getstoredindex(a::AbstractArray, I::Int...) = getindex(a, I...)
setstoredindex!(a::AbstractArray, value, I::Int...) = setindex!(a, value, I...)
a = zeros(2, 2)
@interface SparseArrayInterface() a[1, 1] = 2
@test @interface(SparseArrayInterface(), a[1, 1]) == 2
@test @interface(SparseArrayInterface(), a[2, 1]) == 0
@test @interface(SparseArrayInterface(), a[1, 2]) == 0
@test @interface(SparseArrayInterface(), a[2, 2]) == 0Test PassedThis page was generated using Literate.jl.