如何实现诸如 Union 的具有可变类型参数的抽象类型


#1

Union 类型的类型参数的个数是可变的,即 Union{Int}Union{Int, Float} 都是 Union 的子类型,类似于 Union 的类型该如何定义?


#2

不能定义custom的union吧。你有具体的user case么?一般这种情况用duck type+trait就行了。

union的话你也可以先定义类型再在外面union。


#3

下面是一些数学性质:

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{}


#4

这是典型的trait,你需要定义trait。


#5

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是更直接的做法,从现有给出的信息,不能判断你具体的需求是什么。


#6

我大概明白我遇到的问题了,之前我想得太一般了,没有利用一些已知的信息和经验,你这样写一般来说就能够避免自定义类型了。