Union
类型的类型参数的个数是可变的,即 Union{Int}
、Union{Int, Float}
都是 Union
的子类型,类似于 Union
的类型该如何定义?
不能定义custom的union吧。你有具体的user case么?一般这种情况用duck type+trait就行了。
union的话你也可以先定义类型再在外面union。
下面是一些数学性质:
abstract type MathProperty end
struct Finiteness{T<:MathStruct} <: MathProperty end
struct Commutativity{T<:MathStruct, op<:Function} <: MathProperty end
struct Associativity{T<:MathStruct, op<:Function} <: MathProperty end
struct Idempotent{T<:MathStruct, op<:Function} <: MathProperty end
struct Group{T<:MathStruct, Multiplication<:Function} <: MathProperty end
abstract type Ring{T<:MathStruct, Addition<:Function, Multiplication<:Function} <: MathProperty end
struct Field{T<:MathStruct, Addition<:Function, Multiplication<:Function} <:
Ring{T, Addition, Multiplication} end
我需要根据运算符具有的性质来定义具体的实现,而每个运算符用到的数学性质有可能不同、也有可能一次要用几个。在语义上,我需要一个行为与 Union
恰好相反的类型 Intersection
,比如 Intersection{Commutativity, Associativity} <: Intersection{Commutativity} <: Intersection{}
。
这是典型的trait,你需要定义trait。
把 Intersection{Commutativity, Associativity}
记为 CommutativityAndAssociativity
, 期望的结果是:
both CommutativityAndAssociativity <: Commutativity
and CommutativityAndAssociativity <: Associativity
成立。这个特性等价于要 Julia 支持多继承。目前,Julia解决这类问题的方法是 trait (i.e. 手动定义一个仅用于dispatch的函数):
# define the intersection type as a subtype of MathProperty
struct CommutativityAndAssociativity <: MathProperty end
# trait function
has_commutativity(x::CommutativityAndAssociativity) = Commutativity
has_commutativity(x::Commutativity) = Commutativity
has_associativity(x::CommutativityAndAssociativity) = Associativity
has_associativity(x::Associativity) = Associativity
# implementations
_impl_featuring_commutativity(::Type{Commutativity}, op) = "op is commutative"
impl_featuring_commutativity(op::Operator{T}) where {T<:MathProperty} = _impl_featuring_commutativity(has_commutativity(T), op)
# 当 T 为 CommutativityAndAssociativity 和 Commutativity 时,都可以正确dispatch,结果好像是 `CommutativityAndAssociativity <: Commutativity`
_impl_featuring_associativity(::Type{Associativity}, op) = "op is associative"
impl_featuring_associativity(op::Operator{T}) where {T<:MathProperty} = _impl_featuring_associativity(has_associativity(T), op)
# 当 T 为 CommutativityAndAssociativity 和 Associativity 时,都可以正确dispatch,结果好像是 `CommutativityAndAssociativity <: Associativity `
X-ref: 如何给一个支持某种interface的参数加类型约束?
BTW, trait 的用处是在定义新类型CommutativityAndAssociativity
时,只要额外的定义相应的trait function就可以将现有implementation代码复用,这类似多继承中复用父类的方法。若不需要这类复用,那么设计一个死的type hierarchy+multiple dispatch是更直接的做法,从现有给出的信息,不能判断你具体的需求是什么。
我大概明白我遇到的问题了,之前我想得太一般了,没有利用一些已知的信息和经验,你这样写一般来说就能够避免自定义类型了。