Reputation: 323
I am trying to use a specific member of a discriminated union as a parameter type. For example:
type SomeUnion =
| A of int * int
| B of string
type SomeType(A(i, j)) =
member this.I = i
member this.J = j
let a = A(10, 20)
let instance = SomeType(a)
but this is illegal syntax, and complains with "Unexpected symbol '(' in type definition" for SomeType's param list. This is valid syntax:
let doSomethingWithA (A(i, j)) = i + j
but the type signature is SomeUnion -> int
rather than A -> int
, and it complains about incomplete pattern match (understandable given the signature).
So is this possible? I believe in F# union members are compiled to CLR classes, so seems theoretically possible, but is it practically (i.e. without using something like reflection)? Otherwise I suppose you have to do the manual OOP way which is more verbose, and can't guarantee complete matches.
Upvotes: 6
Views: 1846
Reputation: 2764
As wmeyer points out, the "A" case of the DU is not a type at all, but is just a component of the union type.
If you really want to reuse one of the union cases on its own, I can't see any alternative to making it an explicit standalone type.
Option one is to just use a type alias:
type TypeA = int * int
type SomeUnion =
| A of TypeA
| B of string
type SomeType(a:TypeA) =
let (i,j) = a
member this.I = i
member this.J = j
let a = (10, 20)
let instance = SomeType(a)
I have added the TypeA
annotation in the constructor for clarity.
The second option is to wrap it in a single case DU.
type TypeA = TA of int * int
type SomeUnion =
| A of TypeA
| B of string
type SomeType(a:TypeA) =
let (TA(i,j)) = a
member this.I = i
member this.J = j
let a = TA (10, 20)
let instance = SomeType(a)
(Note that the match let (TA(i,j)) = a
has to have extra parens so that it is not confused with a function.)
Upvotes: 6
Reputation: 3496
I agree that it is surprising that you cannot pattern match the constructor argument. It does work with normal members.
Maybe you can do an explicit match in the constructor, to get a runtime error if the value is wrong:
type SomeType(a) =
let i, j = match a with | A(k, l) -> k, l
member this.I = i
member this.J = j
But otherwise, one must understand that A
is not a type. So it is not surprising that the type of doSomethingWithA
is not as you expected. And you would have to live with the incomplete pattern match warnings.
Upvotes: 7
Reputation: 25844
My F# is a little rusty, but maybe you could do something like this (tested with f# 2 on mono)?
type SomeUnion =
| A of int * int
| B of string
type SomeType(i:int, j:int) =
member this.I = i
member this.J = j
let createSomeType(SomeUnion.A(i,j)) = new SomeType(i,j)
let a = A(10, 20)
let instance = createSomeType(a)
Upvotes: 5