2016-02-25 4 views
5

Dies ist ein Versuch, einen Teil der Frage, die ich here gebeten, zu vereinfachen:Wie kann ich eine Eigenschaft in Julia mit offenen Typen schreiben?

Ich möchte einige Code schreiben, der auf Arten arbeiten, die bestimmte Kriterien erfüllen, gewährleistet ist. Lassen Sie uns heute sagen, ich schreibe einige 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) 

Lauf dies funktioniert:

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

julia> combine_two_steps(x) 
7.5 

an einem anderen Tag Dann schreibe ich etwas mehr 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 

Und was weißt du, mein generic Mähdrescherfunktion funktioniert immer noch!

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

julia> combine_two_steps(y) 
166.5 

Hurra! Aber, sagen wir, es ist eine lange Nacht und ich versuche das WIEDER an einem dritten Beispiel zu machen. Ich erinnere mich daran, step_one zu implementieren, aber ich vergesse zu implementieren step_two!

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

Jetzt, wenn ich dies ausführen, bekomme ich einen Laufzeitfehler!

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

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

Nun, ich arbeite für einen Manager, der mich töten, wenn ich jemals einen Laufzeitfehler erhalten. Also, was ich tun muss, um mein Leben zu retten, ist, eine Eigenschaft zu schreiben, die im Wesentlichen besagt: "Wenn der Typ diese Eigenschaft implementiert, ist es sicher, combine_two_steps aufzurufen."

Ich möchte

so etwas wie
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 schreiben, dann würde ich wissen, dass wenncombine_two_steps ist immer geschickt, dann wird laufen, ohne einen Fehler zu erhöhen, dass diese Methoden don Ich existiere nicht.

Äquivalent, istrait(ImplementsBothSteps{X}) (ist wahr) entspricht combine_two_steps wird ohne Fehler-von-Nichtexistenz-von-erforderlichen-Methoden ausgeführt.

Aber, wie jeder weiß, kann ich diese Merkmaldefinition nicht verwenden, weil Y keine Bedeutung hat. (In der Tat, seltsam genug, um den Code kompiliert ohne Fehler,

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 

aber die Typen erfüllen nicht die Eigenschaft, auch wenn die Methoden für einige Y existieren.) Mein erster Gedanke ist, ich Y zu so etwas wie Any ändern

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

aber dies nicht zu b/c der Any wirklich soll so etwas wie Some, nicht wörtlich die Any Typ sein (da ich nie ein Verfahren step_two implementiert, die jede Art als Eingabe könnte), aber einige besondere Typ, der in beiden Zeilen geteilt wird!

Die Frage ist also: Was würden Sie in dieser Situation tun?Sie möchten eine "Spezifikation" (hier in Form des durch die Eigenschaft ausgedrückten Vertrags) so weitergeben, dass jeder Programmierer, der die Spezifikation erfüllt, garantiert Ihre Funktion combine_two_steps verwenden kann, aber die Spezifikation hat im Wesentlichen einen Existenzquantifizierer in seiner Definition.

Gibt es eine Problemumgehung? Ein besserer Ansatz zum Schreiben der "Spezifikation" (zB "Traits nicht verwenden, etwas anderes verwenden?") Usw.

Übrigens klingt es vielleicht künstlich, aber die oben verlinkte Frage und diese Frage kommen regelmäßig in einem Projekt, an dem ich gerade arbeite. Ich bin im Wesentlichen an einer Straßensperre fest, die durch dieses Problem verursacht wird, und habe hässliche Problemumgehungen, die von Fall zu Fall funktionieren, aber keine Annäherung an den allgemeinen Fall.

Antwort

1

Generalisieren auf den Vorschlag in meiner Frage der Verwendung von Any kann eigentlich auch funktionieren, obwohl es hässlich ist und nicht wirklich auf den Punkt kommen. Angenommen, Sie haben bereits implementierten Methoden

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

Dann können Sie die Eigenschaft als

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

schreiben und fügen Sie einfach eine Dummy-Methode

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

Dieses in einem Makro eingewickelt werden kann und um die Wiederholung des Musters zu speichern, und sobald diese Methode implementiert ist, ist das Merkmal erfüllt. Es ist ein Hack, den ich benutzt habe (und das funktioniert), es ist ziemlich einfach, aber die Lösung ist nicht im Sinne meiner Frage.

+0

Können Sie bitte die Details veröffentlichen. –

1

Ist dies zufriedenstellend:

@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 

Mit diesen Charakterzug Definitionen haben wir:

julia> istrait(ImplementsStep2{Int64}) 
true 

julia> istrait(AnotherImplementsBothSteps{Example}) 
true 

Der Trick ist @constraints zu verwenden, um die nicht-einfach Sachen im Grunde tun. Und Base.return_types zu verwenden, um die Rückgabetypen für eine Methode zu erhalten. Zugegeben, das ist ein bisschen wie ein Hack, aber das ist es, was meine Grabung hervorgebracht hat. Vielleicht wird eine zukünftige Version von Traits.jl bessere Werkzeuge dafür haben.

Ich habe any in der Merkmaldefinition verwendet. Das ist ein bisschen nachlässig. Die Verwendung von all kann strenger sein, aber die Einschränkung besser darstellen, je nachdem, welche Stufe der Kompilierzeitprüfung gewünscht ist.

Natürlich erlaubt Julia gute Introspektion und try ... catch ermöglicht alle diese Überprüfung zur Laufzeit.

+0

Die Strategie der Verwendung von Base.return_types ist interessant. Sie haben effektiv Code geschrieben, der den Schritt "Existenzieller Quantifizierer" reflektiv implementiert. Ich mag das. Ich werde darüber noch ein wenig nachdenken. Diese ganze Sache könnte in ein "Meta" -Makro verpackt werden, um den Prozess für den Endbenutzer einfacher zu machen. Ich möchte jedoch die Leistung dieser Strategie untersuchen. Kennen Sie Ihren Kopf, wenn dies dazu führt, dass die Methodentransmission bei Merkmalen einen Leistungseinbruch verursacht? – Philip

+0

Kann der Compiler während des Kompilierens auf die Typen der Parameter schließen, sollte kein Performance-Treffer auftreten (außer für die initiale Generierung und Kompilierung der beteiligten Funktionen). Auf der anderen Seite, wenn die Typen unbekannt sind, könnte es möglicherweise ein Performance-Hit sein, um die richtige Funktion abzuleiten, aber die generierte Funktion wird zwischengespeichert, also würde dies hoffentlich auch nur einmal gemacht werden.Dies ist die übliche Geschichte in Julia, wenn Typen stabil und abgeleitet sind, wird eine Höchstleistung erreicht (BTW eine große Verbesserung für 'mauro3', der' Traits.jl' geschrieben hat). –

+0

Die von 'Traits.jl' verwendeten Methoden werden im' README.md' des github repo ausführlich erklärt (Link: https://github.com/mauro3/Traits.jl) –

Verwandte Themen