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
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.
Foo, the case contains one element of typeint * stringBar, the case contains two elements of typeintandstringThe 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:
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):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 * intas both n-ary union case and unary case with a tuple (but without the bracketted type( type )).