2015-01-23 5 views
6

Ich habe kämpfen, um den Code in der RoP Artikel von Scott Wlaschin zu verstehen:Der Versuch, die Wahl Art in F # verstehen

http://fsharpforfunandprofit.com/posts/railway-oriented-programming-carbonated/

Er Verwendung der # Choice1Of2 & Choice2Of2 Typen in F macht. Ich habe versucht, sich mit zu kommen, wie diese Dinge zu nutzen, indem sie das Debuggen, wenn ich über das folgende Szenario kam:

module TestModule 
open Microsoft.VisualStudio.TestTools.UnitTesting 

// generic union type (like Choice1Of2, I think) 
type Things<'a> = 
    | Thing of 'a 

// explicit union type (for comparison) 
type Numbers = 
    | Integer of int 

[<TestClass>] 
type Tests() = 

    // method to make a Choice1Of2 (from the article) 
    let makeChoice (a : string) : Choice<string, 'a> = 
     Choice1Of2 a 

    [<TestMethod>] 
    member public this.WhyYouNoHaveItemValueAndStuff() =  
     let choice1 = Thing "test"   // debug = Thing "this" 
     let choice2 = Integer 3    // debug = Integer 3 
     let choice3 = makeChoice "test"  // debug = Choice1Of2 w/Item = "test" 
     let choice4 = Choice1Of2 "test"  // debug = [email protected] ??? 

     // bogus test stuff below here 
     let choices = (choice1, choice2, choice3, choice4) 
     Assert.IsNotNull(choices) 

Warum ist es, dass, wenn ich einen Choice1Of2 direkt (choice4) machen, weiß ich nicht Erhalte das gleiche Debugging-Ergebnis wie die Auswahl 3. Warum benutzt man eine Methode, um choice3 zu machen, um die gleiche Art von Ergebnis zu erhalten wie choice1 & 2?

EDIT:

Es scheint, dass choice4 zu diesem Wechsel:

let choice4 : Choice<string, Object> = Choice1Of2 "test" 

klappt es. Es ist mir völlig unklar, warum ich das dort brauche. Die rechte Seite der Zuweisung ist so klar wie der Tag, um den Typ festzulegen.

+1

Nicht sicher, was genau Typ-Inferenz dort tut, aber es ist wahrscheinlich durch den undefinierten Typ dieser Werte verwirrt. Der Laufzeittyp ist 'Choice ' für beide, und sie scheinen in der Verwendung identisch zu sein. Mit einer Typannotation wie ': Choice ' auf 'choice3' und' choice4' sehen sie auch in den Debug-Informationen identisch aus. – Vandroiy

+0

@Vandroiy Thx für die Antwort. Das zwingt tatsächlich das richtige Verhalten. Immer noch seltsam, dass es notwendig ist. – TBone

+2

Es scheint mir, dass es nicht wirklich um korrektes Verhalten geht, sondern darum, was Sie im Debugger beobachten.Ich nehme an, dass Sie sehen, dass choice4 einen unvollständig abgeleiteten Typ hat und dass die interne Darstellung eines unvollständig abgeleiteten Typs sich von der eines Typs mit einem generischen Parameter unterscheidet. Wenn Sie nicht einen Fall finden, in dem diese Bindungen im Code anders ausgewertet werden (anders als im Debugger), glaube ich nicht, dass dies mehr als ein VS-Implementierungsdetail ist. –

Antwort

5

Die Definition des Wahl-Typ ist als

type Choice<'a, 'b> = 
     | Choice1Of2 of 'a 
     | Choice2Of2 of 'b 

So folgt, wenn Sie eine Instanz der Wahl Typ konstruieren, wie Sie in choice4 tun nur eine dieser Beine verwenden, das im Wesentlichen ein Loch verlässt (die Typ Beschreibung 'b), die der Debugger zu füllen hat, in der Tat zur Laufzeit kann es nicht einmal sicher sein, dass der Typ tatsächlich Choice<'a,'b> ist, so dass Sie einen temporären Typ erhalten, der durch eine FSharpFunc dargestellt wird. Auf ähnliche Weise würde der Typ-Inferenz-Mechanismus melden, wobei 'a das Loch darstellt, bis die Instanz übereinstimmt, über die Sie dann gezwungen wären, die andere Seite einzugeben. Bereitstellen der Typsignatur inline, z.

Bedeutet, dass Sie das Loch füllen und dem Debugger genügend Informationen zur Verfügung stellen, um den Typ korrekt darzustellen.

BEARBEITEN (siehe Kommentare): choice3 wird als Choice1Of2<string,obj> dargestellt, weil obj als Top (allgemeinster Typ) betrachtet wird. Dies ist der Rückgabetyp der Typrückschlussmechanismen, der verwendet wird, wenn alle anderen Optionen ausgeschöpft sind. Wenn wir etwas Code hinzufügen, z.

let result = 
    match choice3 with 
    | Choice1Of2 t -> t.GetHashCode() 
    | Choice2Of2 t -> t 

dann werden wir Choice<string, int> erhalten, wie die Art der GetHashCode()int ist somit das Ergebnis der zweiten Match-Klausel muss int für die Art des Ergebnisses seiner Ausdruck konsistent sein zu lassen.

+0

Ich glaube nicht, dass das die ganze Erklärung ist. Das Problem ist, dass "a" auch ein "Loch" darstellt. Ich vermute, die Antwort ist, dass der Compiler immer noch wartet, um zu sehen, ob er auf andere Informationen stoßen wird, die ihm erlauben, den Typparameter konkreter anzugeben, und dass er eine andere Darstellung für "Loch, das noch zu füllen ist" hat tut für "Loch, das nicht gefüllt wird". –

+0

Aber Sie haben bereits 'a zu string durch Instanziieren von Choice1Of2 "Test" eingeschränkt. Das ist also kein Loch mehr. –

+0

Ich spreche von dem anderen "A", dem "A" in "Choice ". Aber jetzt, wo ich diesen Code in VS habe, sehe ich, dass 'choice4' so eingegeben wird, wie ich es erwarte (' Choice '), und es ist' choice3', was ich ein wenig überraschend finde. (Ich bin auch neu in F #, aber ich habe mit OCaml gearbeitet.) Ich bin mir nicht sicher, an welchem ​​Punkt es entscheidet, dass der zweite Parameter von 'choice3'' obj' sein sollte. –

Verwandte Themen