2016-02-25 5 views
5

これは私がhereを尋ねた質問の一部を簡素化する試みです。のは、私はいくつかのコードを書き、今日言ってみましょう:どのようにしてJuliaでOpen-ended型のTraitを書くことができますか?</p> <p>私は一定の基準を満たすタイプの上で動作することが保証されていくつかのコードを書きたい:

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) 

は、この作品の実行:

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> x = Example("Hi!") 
Example("Hi!") 

julia> combine_two_steps(x) 
7.5 

はその後、別の日、私はいくつかのより多くのコードを書きます機能を組み合わせる機能はまだあります!

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

julia> combine_two_steps(y) 
166.5 

ハレイ!しかし、それは深夜で、私はもう一度これを第3の例でやろうとしています。私は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) 

私は今まで実行時エラーが発生した場合、私を殺してくれるマネージャーのために働いています。だから、私の人生を救うために必要なことは、「この種の型がこの種の形質を実装すれば、combine_two_stepsに電話をかけることは安全です」と言う本質的な特徴を書くことです。

は、私はその後、私はを派遣場合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のようなものを書きたいこと意志エラーを出さずに実行することを、これらのメソッドドン存在しない。

これに相当すると、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のために存在していても形質を満たしていない。)私が最初に考えたのは、私がAnyのようなものにYを変更することができますです

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

が、これは、B/C Anyは本当にSome、ない文字通りAnyタイプ(私は、入力として任意の型を取ることができる方法step_twoを実施したことがないので)のようなものになるはずが、いくつかは、特定されすぎて失敗します両方の行で共有されるタイプです。

だから、質問です:あなたはこのような状況で何をしますか?仕様を満たすプログラマーがあなたの関数combine_two_stepsを使用できることが保証されているように、スペック(ここではTraitのコントラクトの形で)を渡したいが、スペックは本質的に存在量限定子その定義において。

回避策はありますか? 「仕様を書いて、他のものを使用しない」などの「スペック」を書く方が良いでしょう。

ところで、上記の質問とこの質問は来ているかもしれません私が取り組んでいるプロジェクトでは定期的に活動しています。私は本質的にこの問題によって引き起こされたロードブロッキングに悩まされており、ケースバイケースでは動作しない醜い回避策を持っていますが、一般的なケースではアプローチはありません。

答えて

1

Anyを使用して私の質問の提案を一般化することは実際には動作しますが、実際には問題はありません。あなたはすでに、あなたが

@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 

ダミーメソッドを追加することができます方法

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

を実装していると仮定パターンを繰り返すのを節約し、そのメソッドが実装されるとその特性が満たされる。それは私が使ってきたハックです(そしてそれはうまくいく)b/cそれはかなり簡単ですが、解決策は私の質問の精神ではありません。

+0

詳細を投稿できますか? –

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は実行時にこのすべてのチェックを行うことができます。

+0

Base.return_typesを使用する戦略は面白いです。あなたは、 "存在量限定子"ステップを反映したコードを効果的に書いています。私はこれが好き。もう少し考えてみるつもりです。実際、このプロセス全体をエンドユーザに単純化するために、この全体を「メタ」マクロにまとめることができます。私はこの戦略のパフォーマンスを調査したい。これが特性のメソッドディスパッチがパフォーマンスヒットを起こす場合は、あなたの頭の上から知っていますか? – Philip

+0

コンパイラがコンパイル中にパラメータの型を推論できる場合は、パフォーマンスヒットは発生しません(関連する関数の初期生成とコンパイルを除く)。一方、タイプが不明な場合は、適切な関数を推測するためのパフォーマンス・ヒットになる可能性がありますが、生成された関数はキャッシュされています。これは、タイプが安定して推論され、最高のパフォーマンスが得られるJuliaの一般的な話です(BTWはTraits.jlを書いたmauro3の大きなアップヴォートです)。 –

+0

'Traits.jl'で使用されるメソッドについては、githubリポジトリの' README.md'で詳しく説明しています(リンク:https://github.com/mauro3/Traits.jl) –

関連する問題

 関連する問題