我如何用开放式类型在Julia中编写特质? [英] How can I write a Trait in Julia with open-ended types?

查看:163
本文介绍了我如何用开放式类型在Julia中编写特质?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是试图简化我问的一个问题的一部分



我想写一些保证可以处理类型的代码符合某些标准。假设我今天写了一些代码:

 不可变例子
whatever :: ASCIIString
end
函数step_one(x :: Example)
length(x.whatever)
end
函数step_two(x :: Int64)
(x * 2.5):: Float64
结束
函数combine_two_steps {X}(x :: X)
middle = step_one(x)
结果= step_two(中)
结果
结尾
x = Example(Hi!)
combine_two_steps(x)

  julia> x =示例(Hi!)
示例(Hi!)

julia> combine_two_steps(x)
7.5

然后我再写一些代码:

 不可变TotallyDifferentExample 
whatever :: Bool
end
函数step_one(x :: TotallyDifferentExample)
如果x.whatever
Hurray
else
Boo
结束
结束
函数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

 不可变ForgetfulExample 
whatever :: Float64
end
函数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`没有方法匹配step_two(:: Float64)

现在,我为一名经理工作,如果我遇到运行时错误,他会杀了我。所以我需要做的是拯救我的生命,就是写一个基本上说如果这个类型实现这个特性的特质,那么可以安全地调用 combine_two_steps



我想使用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)
结果
结束



b / c然后我知道 if em> combine_two_steps 会被分派,那么它将运行而不会引发这些方法不存在的错误。
$ b

等价地, istrait(ImplementsBothSteps {X})(为true)相当于 combine_two_steps 将运行时不会出现错误 - 不需要的方法。但是,大家都知道,我不能使用这个特征定义,因为 Y 没有意义。 (实际上,奇怪的是代码编译没有错误,

  julia> @traitdef ImplementsBothSteps {X} begin 
step_one (X) - > Y
step_two(Y) - > Float64
end

julia>不可变例子
whatever :: ASCIIString
end

julia>函数step_one(x :: Example)
length(x.whatever):: Int64
end
step_one(带1个方法的通用函数)

julia>函数step_two(x :: Int64)
(x * 2.5):: Float64
end
step_two(带1个方法的通用函数)

julia> istrait(ImplementsBothSteps {Example})
false

即使存在某些 Y 的方法,我仍然可以满足特质。)我的第一个想法是我可以将 Y 更改为某些像任何

 使用特征
@traitdef ImplementsBothSteps {X }开始
s tep_one(X) - >任何
step_two(任何) - > Float64
end

但是这也失败了b / c Any 真的应该是 Some ,而不是字面意义上的 Any 类型(因为我从来没有实现过一种方法 step_two ,它可以接受任何类型的输入),但是在两行中共享一些特定的类型!



所以问题是:你会在这种情况下做什么?你希望传递一个规范(这里以Trait表达的合约的形式),任何满足规范的程序员都可以保证能够使用你的函数 combine_two_steps ,但规范在其定义中基本上有一个存在量词。



有没有解决方法?一个更好的方法来编写规范(例如不要使用特质,使用别的东西?)等等。

顺便说一下,但是上面提到的问题和这个问题在我正在开展的一个项目中定期提出。我基本上被这个问题困住了一个障碍,并且有一些丑陋的解决方法,它们可以逐案工作,但是没有办法处理一般情况。 解决方案

在使用任何的问题中推广的建议实际上也可以工作,尽管它很丑并且没有真正到位。假设您已经实施了方法

  step_one(X) - > Y 
step_two(Y) - > Z

然后您可以将特征写为

  @traitdef implements_both_steps begin 
step_one(X) - >任何
step_two(任何) - > Z
end

只需添加一个虚拟方法

 函数step_two(x :: Any)
typeof(x)== Y? step_two(x :: Y):error(Invalid type)
end

也可以包含在宏中以节省重复模式,然后一旦实现该方法,性状就满足了。这是一个黑客,我一直在使用(和工程)B / C它相当简单,但解决方案不符合我的问题的精神。


This is an attempt to simplify one part of the question I asked here:

I want to write some code that is guaranteed to work on types that meet certain criteria. Let's say today I write some code:

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)

Running this works:

julia> x = Example("Hi!")
Example("Hi!")

julia> combine_two_steps(x)
7.5

Then another day I write some more code:

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

And what do you know, my generic combine function still works!

julia> y = TotallyDifferentExample(false)
TotallyDifferentExample(false)

julia> combine_two_steps(y)
166.5

Hurray! But, say it's a late night and I'm trying to do this AGAIN on a third example. I remember to implement step_one, but I forget to implement step_two!

immutable ForgetfulExample
    whatever::Float64
end
function step_one(x::ForgetfulExample)
    x.whatever+1.0
end

Now when I run this, I'm going to get a run-time error!

julia> z = ForgetfulExample(1.0)
ForgetfulExample(1.0)

julia> combine_two_steps(z)
ERROR: MethodError: `step_two` has no method matching step_two(::Float64)

Now, I work for a manager who will KILL ME if I ever get a run-time error. So what I need to do to save my life is to write a Trait that essentially says "if the type implements this trait, then it's safe to call combine_two_steps."

I want to write something like

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 then I'd know that if combine_two_steps is ever dispatched, then it will run without raising an error that these methods don't exist.

Equivalently, istrait(ImplementsBothSteps{X}) (being true) is equivalent to combine_two_steps will run without error-from-nonexistence-of-required-methods.

But, as everybody knows, I can't use that trait definition, because Y has no meaning. (In fact, oddly enough the code compiles without error,

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

but the types don't satisfy the trait even though the methods exist for some Y.) My first thought is I can change Y to something like Any

using Traits
@traitdef ImplementsBothSteps{X} begin
    step_one(X) -> Any
    step_two(Any) -> Float64
end

but this fails too b/c the Any really is supposed to be something like Some, not literally the Any type (since I never implemented a method step_two that could take any type as input), but some particular type that's shared across both lines!

So, the question is: what would you do in this situation? You want to pass around a "spec" (here in the form of the contract expressed by the Trait) such that any programmer anywhere who meets the spec is guaranteed to be able to use your function combine_two_steps, but the spec essentially has an existential quantifier in its definition.

Is there a workaround? A better approach to writing the "spec" (e.g. "Don't use Traits, use something else"?) Etc.

By the way, it may sound contrived, but the above-linked question and this question are coming up regularly in a project I'm working on. I'm essentially stuck at a roadblock caused by this problem and have ugly workarounds that work case-by-case, but no approach to the general case.

解决方案

Generalizing on the suggestion in my question of using Any actually can work also, although it's ugly and doesn't really get to the point. Suppose you have already implemented methods

step_one(X) -> Y
step_two(Y) -> Z

Then you can write the trait as

@traitdef implements_both_steps begin
    step_one(X) -> Any
    step_two(Any) -> Z
end

And just add a dummy method

function step_two(x::Any)
    typeof(x)==Y ? step_two(x::Y) : error("Invalid type")
end

This can be wrapped up in a macro as well to save on repeating the pattern, and then once that method is implemented the trait is satisfied. It's a hack that I've been using (and that works) b/c it's fairly straightforward, but the solution is not in the spirit of my question.

这篇关于我如何用开放式类型在Julia中编写特质?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆