A feature or a bug?
An innocent-looking pair of parentheses ends up influencing the runtime representation of discriminated union types.
I would prefer not to have this. The two representations seem conceptually redundant, it would be better to just pick the faster one.
Given this foo.fs
:
#light
open Microsoft.FSharp.Reflection
type A1 = A1 | B1 of (A1 * A1)
type A2 = A2 | B2 of A2 * A2
printfn "%A" ((FSharpType.GetUnionCases(typeof<A1>)
|> Array.map (fun x -> x.GetFields())));;
printfn "%A" ((FSharpType.GetUnionCases(typeof<A2>)
|> Array.map (fun x -> x.GetFields())));;
Run fsi --quiet --exec foo.fs
to produce this:
[|[||]; [|Microsoft.FSharp.Core.Tuple`2[FSI_0001.Foo+A1,FSI_0001.Foo+A1] B11|]|]
[|[||]; [|FSI_0001.Foo+A2 B21; FSI_0001.Foo+A2 B22|]|]
I found this code while testing a library that produced different results depending on the presence or absence of the parentheses.
Update: A feature! Thanks to Zedd. I should know my specs and my OCaml:
Objective Caml version 3.10.2 # type c = C of int * int;; type c = C of int * int # match C(1, 2) with | C x -> x;; The constructor C expects 2 argument(s), but is here applied to 1 argument(s) # type c = C of (int * int);; type c = C of (int * int) # match C(1, 2) with | C x -> x;; - : int * int = (1, 2) #
From the F# Language Specification 1.9.6 Draft:
ReplyDeleteParentheses are significant in discriminated union definitions:
type c = C of int * int
and
type c = C of (int * int)
differ. The parentheses are used to indicate that the constructor takes on argument that is a first-class pair value. Without the parentheses you must write
match c with
| C (x,y) -> ...
Listed under section 9.3: Union Types.
You can quickly confirm the differing behavior with fsi.exe
A1 can be matched with A1, B1(x,y) or B1(t). x and y will be matched A1 respectively. t will be matched as a tuple of A1 * A1.
A2 can only by matched by A2 or B2(x,y)
Hope that helps.
Thanks a lot! It does.
ReplyDeleteJust checked with OCaml, same behavior.
SO, it IS a feature.