这是为了简化我问here问题的一部分:
我想编写一些保证可以处理符合特定条件的类型的代码。我们今天说我写了一些代码:
immutable Example
whatever::ASCIIString
end
function step_one(x::Example)
length(x.whatever)
end
function step_two(x::Int64)
(x * 2.5)::Float64
end
function combine_two_steps{X}(x::X)
middle = step_one(x)
result = step_two(middle)
result
end
x = Example("Hi!")
combine_two_steps(x)
运行此功能:
julia> x = Example("Hi!")
Example("Hi!")
julia> combine_two_steps(x)
7.5
然后另一天我写了更多代码:
immutable TotallyDifferentExample
whatever::Bool
end
function step_one(x::TotallyDifferentExample)
if x.whatever
"Hurray"
else
"Boo"
end
end
function step_two(x::ASCIIString)
(Int64(Char(x[end])) * 1.5)::Float64
end
你知道什么,我的通用组合功能仍然有效!
julia> y = TotallyDifferentExample(false)
TotallyDifferentExample(false)
julia> combine_two_steps(y)
166.5
乌拉!但是,说这是一个深夜,我试图在第三个例子上再次这样做。我记得要实施step_one
,但我忘了实施step_two
!
immutable ForgetfulExample
whatever::Float64
end
function step_one(x::ForgetfulExample)
x.whatever+1.0
end
现在,当我运行此操作时,我将遇到运行时错误!
julia> z = ForgetfulExample(1.0)
ForgetfulExample(1.0)
julia> combine_two_steps(z)
ERROR: MethodError: `step_two` has no method matching step_two(::Float64)
现在,我为一位经理工作,如果我遇到运行时错误,他将会杀了我。所以我需要做的就是拯救我的生命就是写一个基本上说'#34;如果类型实现这个特性的特性,那么调用combine_two_steps
是安全的。"
我想写点像
using Traits
@traitdef ImplementsBothSteps{X} begin
step_one(X) -> Y
step_two(Y) -> Float64
end
function combine_two_steps{X;ImplementsBothSteps{X}}(x::X)
middle = step_one(x)
result = step_two(middle)
result
end
b / c然后我知道如果 combine_two_steps
,那么 提出这些方法不存在的错误。
等效地,istrait(ImplementsBothSteps{X})
(为真)相当于combine_two_steps
将运行而没有从不存在错误的方法。
但是,众所周知,我不能使用这种特质定义,因为Y
没有意义。 (事实上,奇怪的是,代码编译时没有错误,
julia> @traitdef ImplementsBothSteps{X} begin
step_one(X) -> Y
step_two(Y) -> Float64
end
julia> immutable Example
whatever::ASCIIString
end
julia> function step_one(x::Example)
length(x.whatever)::Int64
end
step_one (generic function with 1 method)
julia> function step_two(x::Int64)
(x * 2.5)::Float64
end
step_two (generic function with 1 method)
julia> istrait(ImplementsBothSteps{Example})
false
但即使某些Y
的方法存在,这些类型也不能满足特征。)我首先想到的是我可以将Y
更改为Any
using Traits
@traitdef ImplementsBothSteps{X} begin
step_one(X) -> Any
step_two(Any) -> Float64
end
但这也失败b / c Any
真的应该是Some
,而不是Any
类型(因为我从未实现过step_two
方法可以采用任何类型作为输入),但是在两条线上共享的某些特定类型!
所以,问题是:在这种情况下你会做什么?你想传递一个" spec" (这里是Trait表达的合同形式),这样任何符合规范的程序员都可以保证能够使用你的函数combine_two_steps
,但规范在其定义中基本上有一个存在量词。 / p>
有解决方法吗?编写"规范"的更好方法(例如"不要使用特征,使用别的东西"?)等等。
顺便说一句,这可能听起来很人为,但上面提到的问题和这个问题在我正在研究的项目中经常出现。我基本上陷入了由此问题引起的障碍,并且具有逐案处理的难看的解决方法,但没有解决一般情况。
答案 0 :(得分:1)
在我使用Any
的问题中推广这个建议实际上也可以起作用,虽然它很难看并且没有真正达到目的。假设您已经实现了方法
step_one(X) -> Y
step_two(Y) -> Z
然后你可以把这个特性写成
@traitdef implements_both_steps begin
step_one(X) -> Any
step_two(Any) -> Z
end
只需添加一个虚拟方法
function step_two(x::Any)
typeof(x)==Y ? step_two(x::Y) : error("Invalid type")
end
这也可以包含在宏中以节省重复模式,然后一旦实现该方法,就满足了特征。这是一个我一直在使用的黑客(而且有用)b / c它相当简单,但解决方案不符合我的问题。
答案 1 :(得分:1)
这是否令人满意:
@traitdef ImplementsStep2{Y} begin
step_two(Y) -> Float64
end
# consider replacing `any` with `all`
@traitdef AnotherImplementsBothSteps{X} begin
step_one(X)
@constraints begin
any([istrait(ImplementsStep2{Y}) for Y in Base.return_types(step_one,(X,))])
end
end
根据这些特征定义,我们有:
julia> istrait(ImplementsStep2{Int64})
true
julia> istrait(AnotherImplementsBothSteps{Example})
true
诀窍是使用@constraints
基本上做非直截了当的事情。并使用Base.return_types
来获取方法的返回类型。不可否认,这有点像黑客,但这就是我的挖掘所提出的。或许Traits.jl
的未来版本可能会有更好的工具。
我在特质定义中使用了any
。这有点松懈。使用all
可能更严格,但更好地表示约束,具体取决于所需的编译时检查级别。
当然,Julia的良好内省和try ... catch
允许在运行时进行所有这些检查。