这是试图简化我提出的问题的一部分 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)
现在,我为一位经理工作,如果我遇到运行时错误,他会杀了我。所以我需要做的来挽救我的生命是写一个 Trait 本质上说“如果类型实现了这个 trait,那么调用
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
),但是某些特定类型在两行之间共享!所以,问题是:在这种情况下你会怎么做?你想传递一个“规范”(这里以 Trait 表示的契约形式),这样任何地方的任何符合规范的程序员都可以保证能够使用你的函数
combine_two_steps
,但规范在其定义中本质上有一个存在量词。有解决方法吗?编写“规范”的更好方法(例如“不要使用特征,使用其他东西”?)等等。
顺便说一句,这听起来可能是人为的,但是上面链接的问题和这个问题在我正在从事的项目中经常出现。我基本上陷入了由这个问题引起的障碍,并且有逐案工作的丑陋解决方法,但没有解决一般情况的方法。
最佳答案
概括我在使用 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 的一种 hack,它相当简单,但解决方案不符合我的问题的精神。
关于generics - 如何使用开放式类型在 Julia 中编写 Trait?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35636248/