我如何调度与两种类型有关的特征,其中第二种满足特征的类型是由第一种特征决定的?

Phi*_*lip 20 traits julia

说我有一个朱莉娅性状,其涉及两种类型:一种类型是一种"基地"类型的可满足一种局部性状,另一种是一个相关联的是唯一地由基类型确定类型.(也就是说,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
Run Code Online (Sandbox Code Playgroud)

现在这里有几个示例类型.请忽略示例的细节; 它们仅仅意味着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
Run Code Online (Sandbox Code Playgroud)

既然我已经定义了这些功能,我就可以做到

@assert istrait(IsProduct{Rope})
@assert istrait(IsProduct{Paper})
Run Code Online (Sandbox Code Playgroud)

现在,如果我定义

function measurements(x::Rope)
    (x.length)::Float64
end

function measurements(x::Paper)
    (x.height,x.width)::Tuple{Float64,Float64}
end
Run Code Online (Sandbox Code Playgroud)

然后我就能做到

@assert istrait(IsProductWithMeasurement{Rope,Float64})
@assert istrait(IsProductWithMeasurement{Paper,Tuple{Float64,Float64}})
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好; 这些运行没有错误.现在,我想要做的是编写如下函数:

@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
Run Code Online (Sandbox Code Playgroud)

一般来说,这个函数是一个例子"我想要使用这样一个事实:作为程序员,我知道BaseType总是关联AssociatedType到帮助编译器进行类型推断.我知道每当我执行某项任务时[在这种情况,get_measurements但一般来说这可以在一堆案例中工作]然后我希望编译器以一致的图案方式推断出该函数的输出类型."

也就是说,例如

do_something_that_makes_arrays_of_assoc_type(x::BaseType)
Run Code Online (Sandbox Code Playgroud)

总是会吐出Array{AssociatedType},并

do_something_that_makes_tuples(x::BaseType)
Run Code Online (Sandbox Code Playgroud)

总会吐出来的Tuple{Int64,BaseType,AssociatedType}.

并且,一对这样的关系适用于所有<BaseType,AssociatedType>; 例如,if BatmanTypeRobinType与之关联SupermanType的基本类型,并且LexLutherType是始终关联的基本类型

do_something_that_makes_tuple(x::BatManType)
Run Code Online (Sandbox Code Playgroud)

将始终输出Tuple{Int64,BatmanType,RobinType},和

do_something_that_makes_tuple(x::SuperManType)
Run Code Online (Sandbox Code Playgroud)

将永远输出Tuple{Int64,SupermanType,LexLutherType}.

所以,理解这种关系,我希望编译器为了速度而理解它.

现在,回到功能示例.如果这是有道理的,你会意识到虽然我给出的函数定义作为一个例子是"正确的",因为它满足这种关系并且编译,它是不可调用的,因为编译器不理解之间的关系X而且M,即使我这样做.特别是,由于M没有出现在方法签名中,因此Julia无法在函数上进行调度.

到目前为止,我唯一想要解决这个问题的方法是创建一种解决方法,我可以动态"计算"相关的类型,我仍然可以使用方法分派来完成这个计算.考虑:

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
Run Code Online (Sandbox Code Playgroud)

然后这确实编译并且可以调用:

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
Run Code Online (Sandbox Code Playgroud)

但是,这是不理想的,因为(一)我每次都要重新定义该地图,即使我觉得好像我已经告诉之间的关系的编译器X,并M通过使它们满足的特质,和(b)据我猜猜 - 也许这是错的; 我没有这个直接证据 --the编译器并不一定能够优化以及我想要的,因为之间的关系X,并M为"隐藏"的函数调用的返回值内.

最后一个想法:如果我有能力,我理想的做法是这样的:

@traitdef IsProduct{X} begin
    isnew(X) -> Bool
    coolness(X) -> Float64
    ? ! M s.t. measurements(X) -> M
end
Run Code Online (Sandbox Code Playgroud)

然后有一些方式来引用唯一见证存在关系的类型,例如

@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
Run Code Online (Sandbox Code Playgroud)

因为这会以某种方式调度.

那么:我的具体问题是什么?我问,鉴于你可能在这一点上明白了我的目标

  1. 有我的代码出这种类型的结构一般,这样我就可以在抽象的高层次有效重复这种设计模式跨越了很多案例,然后程序XM,和
  2. do(1)以这样一种方式,编译器仍然可以最大限度地优化它/知道类型之间的关系,就像我,编码器,我

那我该怎么办呢?我认为答案是

  1. 使用 Traits.jl
  2. 做一些与你迄今为止所做的非常相似的事情
  3. 还做____some聪明的事情,回答者会指出,

但我对这个想法持开放态度,事实上正确的答案是

  1. 抛弃这种方法,你正在以错误的方式思考问题
  2. 相反,以这种方式思考:____ MWWE____

我对表格的答案也非常满意

  1. 您要求的是Julia的"复杂"功能仍在开发中,预计将包含在v0.xy中,所以只需等待......

我不太热心(但仍然好奇地听到)这样的答案

  1. 放弃朱莉娅; 而是使用专为此类事物设计的语言________

我也认为这可能与输入Julia函数输出的问题有关,我认为它也在考虑之中,尽管我无法从这个问题中弄清楚这个问题的确切表示.

l--*_*''' 1

那么参数化函数呢?您可以使用参数化函数来指定类型关系,而不是尝试定义类型之间的关系。您可以定义一个函数get_measurements{T}(similar_items::Array{T}),并指定每个函数的预期输出类型T