Background
I am writing a package that provides some structures of matrices, and I want them to have different properties.
module MyModuleA
export
TypeA,
properties
# function interfaces
properties(::Type{<:AbstractMatrix})::Vector{Symbol} = []
properties(m::AbstractMatrix{}) = properties(typeof(m).name.wrapper)
# struct
struct TypeA{T<:Int} <: AbstractMatrix{T}
n::Int
end
properties(::Type{TypeA}) = [:symmetric]
end
I would like the user to add new types and set properties for it:
module UserModuleA
using ..MyModuleA
import ..MyModuleA: properties
export
TypeB
struct TypeB{T<:Int} <: AbstractMatrix{T}
n::Int
end
properties(::Type{TypeB}) = [:symmetric, :inverse]
end
julia> using .MyModuleA, .UserModuleA
julia> properties(TypeB)
2-element Vector{Symbol}:
:symmetric
:inverse
Question
The general approach has two important steps to set properties for my new type:
import ..MyModuleA: properties
properties(::Type{TypeB}) = [:symmetric, :inverse]
Now we want some new features:
- add a constant vector in
MyModuleA
saving all valid properties - for convenience, avoid user to explicitly import the function
- (addtional) define a type
Property
packing these properties symbolsstruct Property name::Symbol end # then it comes like that which is not a good design properties(::Type{TypeB}) = [Property(:symmetric), Property(:inverse)]
I would like to design a macro to solve these problems. The new code should looks like that:
module MyModuleA
export
TypeA,
@properties,
properties
const PROPERTIES = [:symmetric, :inverse]
# properties macro
macro properties(type::Symbol, ex::Expr)
return quote
properties(::Type{$(esc(type))}) = [Property(property) for property = $esc(ex)]
end
end
# function interfaces
properties(::Type{<:AbstractMatrix})::Vector{Symbol} = []
properties(m::AbstractMatrix{}) = properties(typeof(m).name.wrapper)
# struct
struct TypeA{T<:Int} <: AbstractMatrix{T}
n::Int
end
properties(::Type{TypeA}) = [:symmetric]
end
module UserModuleB
using ..MyModuleA
export
TypeB
struct TypeB{T<:Int} <: AbstractMatrix{T}
n::Int
end
@properties TypeB [:symmetric, :inverse]
end
This macro
macro properties(type::Symbol, ex::Expr)
return quote
properties(::Type{$(esc(type))}) = [Property(property) for property = $esc(ex)]
end
end
does not work, but the import statement is still not removed and it is not stable as the module name may be changed in the future, also it may be called in MyModuleA.
My Attempts
- esc the whole function definition
- @eval the function definition
- add import statement in the quote
All these attempts tries are failed. It seems like a Code Generation inside a macro. The type and list of symbols should be escaped, but the properties function should not be escaped.