Why are parentheses significant in F# type declarations

f#, pattern-matching, types

Solution

I agree this looks quite confusing. As pad explained, the difference between the two declarations is not just syntactical - you are actually defining discriminated union cases that consist of different types.

- In case of `Foo`, the case contains one element of type `int * string`

- In case of `Bar`, the case contains two elements of type `int` and `string`

The two options are very similar, but they are actually different. You can see that if you look at the type definitions in the F# specification. Here are the bits that describe type definitions of discriminated union:

union-type-defn := type-name '=' union-type-cases type-extension-elementsopt

union-type-cases := '|'opt union-type-case '|' ... '|' union-type-case

union-type-case := attributesopt union-type-case-data

union-type-case-data := ident -- null union case ident of type * ... * type -- n-ary union case

Note that the "n-ary union case" consists of multiple elements (`type * ... * type`). A type is defined as follows (not surprisingly, it can be a tuple):

type := ( type ) type -> type -- function type type * ... * type -- tuple type ... -- lots of other types

I do not know why union-type-case-data does not use just unary union case (instead of n-ary) and always treat the elements as tuple. I think that would make perfect sense, but it might be something that F# inherited from OCaml or ML. However, at least the specification explains this!

In fact, I suppose the specification is a bit ambiguous, because you could treat `Foo of int * int` as both n-ary union case and unary case with a tuple (but without the bracketted type `( type )`).

Problem

I'm completely baffled by observing this behaviour in my F# code, here taken from an interactive session: ``` 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 > ``` It seems obvious to me that pattern matching on both Foo and Bar with a single variable should be valid - so I was wondering if anyone knew the reason for this wierd behaviour, or if you like me consider it a bug. Update: Just to clarify, the reported types of the constructors `Foo` and `Bar` are: ``` > Foo;; val it : string * int -> foo = <fun:clo@14-1> > Bar;; val it : string * int -> bar = <fun:clo@13> ``` So certainly, they should accept the same set of valid patterns

Original source