假设我有一个与两种类型相关的朱莉娅特征:一种类型是一种"基础"可以满足某种部分特征的类型,另一种是由基本类型唯一确定的关联类型。 (也就是说,BaseType与> AssociatedType的关系是一个函数。)这些类型一起满足了我感兴趣的复合特征。
例如:
using Traits
@traitdef IsProduct{X} begin
isnew(X) -> Bool
coolness(X) -> Float64
end
@traitdef IsProductWithMeasurement{X,M} begin
@constraints begin
istrait(IsProduct{X})
end
measurements(X) -> M
#Maybe some other stuff that dispatches on (X,M), e.g.
#fits_in(X,M) -> Bool
#how_many_fit_in(X,M) -> Int64
#But I don't want to implement these now
end
现在这里有几个示例类型。请忽略示例的细节;它们仅仅意味着MWE,细节上没有任何相关内容:
type Rope
color::ASCIIString
age_in_years::Float64
strength::Float64
length::Float64
end
type Paper
color::ASCIIString
age_in_years::Int64
content::ASCIIString
width::Float64
height::Float64
end
function isnew(x::Rope)
(x.age_in_years < 10.0)::Bool
end
function coolness(x::Rope)
if x.color=="Orange"
return 2.0::Float64
elseif x.color!="Taupe"
return 1.0::Float64
else
return 0.0::Float64
end
end
function isnew(x::Paper)
(x.age_in_years < 1.0)::Bool
end
function coolness(x::Paper)
(x.content=="StackOverflow Answers" ? 1000.0 : 0.0)::Float64
end
由于我已经定义了这些功能,我可以做到
@assert istrait(IsProduct{Rope})
@assert istrait(IsProduct{Paper})
现在如果我定义
function measurements(x::Rope)
(x.length)::Float64
end
function measurements(x::Paper)
(x.height,x.width)::Tuple{Float64,Float64}
end
然后我可以做
@assert istrait(IsProductWithMeasurement{Rope,Float64})
@assert istrait(IsProductWithMeasurement{Paper,Tuple{Float64,Float64}})
到目前为止一切顺利;这些运行没有错误。现在,我想要做的是编写如下函数:
@traitfn function get_measurements{X,M;IsProductWithMeasurement{X,M}}(similar_items::Array{X,1})
all_measurements = Array{M,1}(length(similar_items))
for i in eachindex(similar_items)
all_measurements[i] = measurements(similar_items[i])::M
end
all_measurements::Array{M,1}
end
一般来说,这个函数是&#34;我想要使用这样一个事实:作为程序员,我知道BaseType
始终与AssociatedType
相关联以帮助具有类型推断的编译器。我知道每当我执行某项任务时[在这种情况下,get_measurements
,但一般来说这可以在一堆案例中工作]然后我希望编译器以一致的图案方式推断该函数的输出类型。 &#34;
即,例如
do_something_that_makes_arrays_of_assoc_type(x::BaseType)
将始终吐出Array{AssociatedType}
和
do_something_that_makes_tuples(x::BaseType)
总是吐出Tuple{Int64,BaseType,AssociatedType}
。
AND,对于{em>所有对<BaseType,AssociatedType>
,有一个这样的关系;例如如果BatmanType
是与RobinType
相关联的基本类型,SupermanType
是LexLutherType
始终关联的基本类型,那么
do_something_that_makes_tuple(x::BatManType)
将始终输出Tuple{Int64,BatmanType,RobinType}
和
do_something_that_makes_tuple(x::SuperManType)
将始终输出Tuple{Int64,SupermanType,LexLutherType}
。
所以,我理解这种关系,我希望编译器为了速度而理解它。
现在,回到功能示例。如果这是有道理的,你会意识到虽然我给出的函数定义是一个正确的&#39;在它满足这种关系并进行编译的意义上,它是不可调用的,因为编译器并不理解X
和M
之间的关系,即使我这样做。特别是,由于M
没有出现在方法签名中,因此Julia无法对该函数进行调度。
到目前为止,我唯一想要解决这个问题的方法是创建一种解决方法,我可以计算&#34;计算&#34;动态关联的类型,我仍然可以使用方法分派来完成这个计算。考虑:
function get_measurement_type_of_product(x::Rope)
Float64
end
function get_measurement_type_of_product(x::Paper)
Tuple{Float64,Float64}
end
@traitfn function get_measurements{X;IsProduct{X}}(similar_items::Array{X,1})
M = get_measurement_type_of_product(similar_items[1]::X)
all_measurements = Array{M,1}(length(similar_items))
for i in eachindex(similar_items)
all_measurements[i] = measurements(similar_items[i])::M
end
all_measurements::Array{M,1}
end
然后确实编译并且可以调用:
julia> get_measurements(Array{Rope,1}([Rope("blue",1.0,1.0,1.0),Rope("red",2.0,2.0,2.0)]))
2-element Array{Float64,1}:
1.0
2.0
但这并不理想,因为(a)我每次都必须重新定义这张地图,即使我觉得我已经已经告诉编译器X
和M
之间的关系X
让他们满足特质,(b)据我所知 - 也许这是错的;我没有这方面的直接证据 - 由于M
和@traitdef IsProduct{X} begin
isnew(X) -> Bool
coolness(X) -> Float64
∃ ! M s.t. measurements(X) -> M
end
之间的关系,编译器不一定能够优化我想要的优势是&#34;隐藏&#34;在函数调用的返回值内。
最后一个想法:如果我有能力,我理想做的事情是这样的:
@traitfn function get_measurements{X;IsProduct{X},IsWitnessType{IsProduct{X},M}}(similar_items::Array{X,1})
all_measurements = Array{M,1}(length(similar_items))
for i in eachindex(similar_items)
all_measurements[i] = measurements(similar_items[i])::M
end
all_measurements::Array{M,1}
end
然后有一些方法可以引用唯一见证存在关系的类型,例如
X
因为这会以某种方式调度。
那么:我的具体问题是什么?我问,鉴于你可能在这一点上明白我的目标是
M
和Traits.jl
的高级摘要编程,
和但我对这个想法持开放态度,事实上正确答案是
我对表格的答案也非常满意
并且我不太热心(但仍然很想听)答案,例如
我也认为这可能与输入朱莉娅的功能输出有关,我也认为它正在考虑之中,尽管我还没有弄清楚这个的确切表现就那个问题而言。