2012-08-23 13 views
6

Sono completamente sconcertato osservando questo comportamento nel mio codice F #, qui tratto da un sessione interattiva:Perché le parentesi significative in F dichiarazioni di tipo #

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 
> 

Sembra ovvio per me che pattern matching su entrambi i Foo e Bar con una singola variabile dovrebbe essere valido - quindi mi chiedevo se qualcuno conoscesse il motivo di questo comportamento strano, o se ti piace considerarlo un bug.

Update: Giusto per chiarire, i tipi riportati di costruttori Foo e Bar sono:

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

Quindi certamente, essi dovrebbero accettare lo stesso insieme di modelli validi

risposta

6

Io sono d'accordo questo sembra abbastanza confusione. Come spiegato da Pad, la differenza tra le due dichiarazioni non è solo sintattica: in realtà stai definendo casi sindacali discriminati composti da tipi diversi.

  • In caso di Foo, caso contiene un elemento di tipo int * string
  • In caso di Bar, caso contiene due elementi di tipo int e string

Le due opzioni sono molto simili, ma sono in realtà diversi. Lo puoi vedere se guardi allo the type definitions in the F# specification. Qui ci sono i bit che descrivono definizioni del tipo di unione discriminati:

unione-tipo-defn: =
    nome-tipo '=' union-type-casi-tipo di estensione-elementi optano

casi-union: =
    '|' opt union-type-case '|' ...'|' union-type-caso

unione-tipo-caso: =
    attributi opt   union-type-case-dati

union-type-case-dati: =
    ident                                                                     - caso unione nulla
    ident di tipo * ... * Tipo           - n- ary union case

Si noti che il "caso unione n-ary" è costituito da più elementi (type * ... * type). Un tipo è definito come segue (non sorprendentemente, può essere una tupla):

tipo: =
    (tipo)
    tipo -> tipo               - tipo di funzione
    tipo * ... * tipo       - tuple tipo
    ...                                                 - un sacco di altri tipi

Non so perché union-type-data-data non usa solo un caso unione unaria (invece di n-ary) e considera sempre gli elementi come tupla. Penso che sarebbe perfettamente logico, ma potrebbe essere qualcosa che F # ha ereditato da OCaml o ML. Tuttavia, almeno la specifica lo spiega!

In realtà, suppongo che la specifica è un po 'ambigua, perché si potrebbe trattare Foo of int * int come entrambi i casi l'unione n-ario e caso unario con una tupla (ma senza il tipo bracketted (type)).

+0

Grazie per una risposta molto approfondita, anche indicando il pezzo rilevante delle specifiche della lingua! Ma temo che consideri ancora più una mostruosa mostruosità, ora che è intenzionale! :-) – plc

+1

@plc - Immagino che il motivo della differenza sia controllare il limite di interoperabilità con altri linguaggi come C#. In tal caso ti potrebbe interessare se devi creare un'istanza di DU inserendo una tupla pre-costruita o passando gli elementi costitutivi singolarmente. – kvb

Problemi correlati