2012-08-23 9 views
6

ich durch die Beobachtung dieses Verhalten in meinem F # -Code völlig ratlos bin, hier aus einer interaktiven Sitzung genommen:Warum sind Klammern signifikant in F # Typdeklarationen

Microsoft (R) F# 2.0 Interactive build 4.0.40219.1 
Copyright (c) Microsoft Corporation. All Rights Reserved. 

For help type #help;; 

> type foo = Foo of (string * int);; 
    type foo = | Foo of (string * int) 

> let f = Foo ("bar",42);; 
    val f : foo = Foo ("bar", 42) 

> match f with Foo x -> x;; 
    val it : string * int = ("bar", 42) 

> type bar = Bar of string * int;; 
    type bar = | Bar of string * int 

> let b = Bar ("baz",21);; 
    val b : bar = Bar ("baz",21) 

> match b with Bar x -> x;; 
    match b with Bar x -> x;; 
    -------------^^^^^ 

stdin(7,14): error FS0019: This constructor is applied to 1 argument(s) but expects 2 
> 

Es scheint mir klar, dass Muster auf beiden Foo passend und Bar mit einer einzelnen Variablen sollte gültig sein - also habe ich mich gefragt, ob jemand den Grund für dieses seltsame Verhalten kannte oder ob Sie mich für einen Fehler halten.

Update: Nur um zu klären, die gemeldeten Arten der Konstrukteure Foo und Bar sind:

> Foo;; 
val it : string * int -> foo = <fun:[email protected]> 
> Bar;; 
val it : string * int -> bar = <fun:[email protected]> 

So sicher, sollten sie den gleichen Satz von gültigen Mustern

Antwort

6

ich zustimmen akzeptieren sieht ganz verwirrend. Wie in Pad erklärt, ist der Unterschied zwischen den beiden Deklarationen nicht nur syntaktisch - Sie definieren diskriminierte Vereinigungsfälle, die aus verschiedenen Typen bestehen.

  • Bei Foo, der Fall ein Element der Art enthält int * string
  • Bei Bar, die jeweils zwei Elemente des Typs enthält int und string

Die beiden Optionen sind sehr ähnlich, aber sie sind tatsächlich anders. Sie können das sehen, wenn Sie the type definitions in the F# specification betrachten. Hier sind die Bits, die Typdefinitionen von diskriminiert Vereinigung beschreiben:

union-Typ-defn: =
    Typname '=' union-Typ-Fälle Typ-Dehn-Elemente entscheiden

Union-Fälle: =
    '|' opt union-type-case '|' ...'|' union-Typ-Fall

union-Typ-Fall: =
    Attribute opt   union-Typ-Fall-Daten

union-Typ-Fall-Daten: =
    ident                                                                     - null Vereinigung Fall
    ident vom Typ * ... * Typ           - n- Verbindungsgehäuse

Beachten Sie, dass der "n-ary union case" aus mehreren Elementen besteht (type * ... * type). Ein Typ ist wie folgt definiert (nicht überraschend, es kann ein Tupel sein):

Typ: =
    (type)
    Typ -> type               - Funktionstyp
    Typ * ... * Typ       - Tupel Typ
    ...                                                 - viele andere Arten

Ich weiß nicht warum union-type-case-data verwendet nicht nur unären union case (statt n-ary) und behandeln die elemente immer als tuple. Ich denke, das wäre vollkommen sinnvoll, aber es könnte etwas sein, das F # von OCaml oder ML geerbt hat. Dies erklärt jedoch zumindest die Spezifikation!

In der Tat, ich nehme die Spezifikation ein bisschen zweideutig ist, da Sie Foo of int * int als sowohl n-ary Vereinigung Fall und einstelligen Fall mit einem Tupel behandeln könnten (aber ohne den bracketted Typen (type)).

+0

Danke für eine sehr gründliche Antwort, sogar das relevante Teil der Sprachspezifikation! Aber ich befürchte, dass ich es jetzt noch mehr als eine schreckliche Ungeheuerlichkeit ansehe, jetzt, wo es beabsichtigt war! :-) – plc

+1

@plc - Ich würde spekulieren, dass der Grund für den Unterschied darin besteht, die Interop-Grenze mit anderen Sprachen wie C# zu kontrollieren. In diesem Fall ist es wichtig, ob Sie eine DU-Instanz erstellen müssen, indem Sie ein vorkonstruiertes Tupel übergeben oder die einzelnen Elemente einzeln übergeben. – kvb

Verwandte Themen