2012-08-23 21 views
6

Estoy completamente desconcertado al observar este comportamiento en mi F # código, aquí tomadas de una sesión interactiva:¿Por qué son paréntesis significativos en F # declaraciones de 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 
> 

Parece obvio que la coincidencia de patrones en tanto Foo y Bar con una sola variable debería ser válida, así que me preguntaba si alguien conocía el motivo de este extraño comportamiento, o si me gusta, lo considero un error.

Actualización: Solo para aclarar, los tipos registrados de los constructores Foo y Bar son:

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

Así que sin duda, se debe aceptar el mismo conjunto de patrones válidos

Respuesta

6

acepto esto parece bastante confuso. Como explicó Pad, la diferencia entre las dos declaraciones no es solo sintáctica: en realidad estás definiendo casos de unión discriminada que constan de diferentes tipos.

  • En caso de Foo, el caso contiene un elemento del tipo int * string
  • En caso de Bar, el caso contiene dos elementos de tipo int y string

Las dos opciones son muy similares, pero en realidad son diferentes. Puede ver eso si mira the type definitions in the F# specification. Éstos son los bits que describen las definiciones de tipo de unión discriminado:

unión de tipo-defn: =
    nombre-tipo '=' union de tipos-tipo casos, la extensión y elementos optan

union-type-cases: =
    '|' opt union-type-case '|' ...'|' unión de tipo caso

unión de tipo caso: =
    atributos opt   de tipo unión de los casos-datos

de tipo unión de los casos-datos: =
    ident                                                                     - nulo caso unión
    ident del tipo * ... * Tipo           - n- caja de unión aria

Tenga en cuenta que el "caso de unión n-ary" se compone de varios elementos (type * ... * type). A tipo se define como sigue (no sorprendentemente, puede ser una tupla):

tipo: =
    (tipo)
    tipo -> tipo               - tipo de función
    tipo * ... * tipo       - tupla tipo
    ...                                                 - un montón de otros tipos

No sé por qué union-type-case-data no utiliza solo unary union case (en lugar de n-ary) y siempre trata los elementos como tuple. Creo que tendría mucho sentido, pero podría ser algo que F # heredó de OCaml o ML. Sin embargo, al menos la especificación explica esto!

De hecho, supongo que la especificación es un poco ambigua, ya que podría tratar Foo of int * int ya que ambos caso la unión n-aria y el caso unario con una tupla (pero sin el tipo bracketted (type)).

+0

Gracias por una respuesta muy completa, incluso señalando la pieza relevante de la especificación de idioma! Pero me temo que ahora lo considero una monstruosidad más grave, ¡ahora que fue intencional! :-) – plc

+1

@plc - Especulo que el motivo de la diferencia es controlar el límite de interoperabilidad con otros lenguajes como C#. En ese caso, le puede interesar si necesita crear una instancia de DU pasando una tupla preconstruida o pasando los elementos constituyentes de forma individual. – kvb

Cuestiones relacionadas