问题
I don't really know what the proper title for this question should be, but:
I have a discriminated union called MyDiscriminatedUnion
in F#
:
type MyDiscriminatedUnion =
| Foo of Foo
| Bar of Bar
where Foo
and Bar
are record types:
type Foo = {
... }
type Bar = {
... }
I created a value of union type Foo
:
let foo = Foo {
... }
The compiler tells me that foo is of type MyDiscriminatedUnion
.
Then I want to pass foo into a function that expects type Foo, not MyDiscriminatedUnion
. Therefore the compiler complains. How do I tell the compiler that foo
is of type Foo
?
I have tried:
let foo:Foo
when constructing the value of union type.
I have tried to "downcast" foo to Foo by:
foo :?> MyDiscriminatedUnion.Foo
but neither of them work.
Please help.
回答1:
This is a common mistake when coming from an OO language: there are no subtypes involved in this code. The fact that you named your union cases the same as the type of the field they contain can make this confusing though, so let me give a slightly different example:
type MyDiscriminatedUnion =
| Its_a_Foo of Foo
| Its_a_Bar of Bar
Its_a_Foo
and Its_a_Bar
are not subtypes, they're union cases. A value of type MyDiscriminatedUnion
is either an Its_a_Foo
, in which case it has a field of type Foo
, or an Its_a_Bar
, in which case it has a field of type Bar
. To know which one it is and get the corresponding field, you need to use pattern matching.
// The function that takes a record of type Foo as argument
let f (x: Foo) = ...
// Our value of type MyDiscriminatedUnion
let myDU = Its_a_Foo { ... }
// Extracting the Foo and passing it to f
match myDU with
| Its_a_Foo foo -> f foo
| Its_a_Bar bar -> // What should we do if it's an Its_a_Bar instead?
// If you're _really_ certain that myDU is Its_a_Foo { ... }, not Its_a_Bar { ... }, you can do this.
// You will get a compile-time warning "FS0025: Incomplete pattern matches on this expression".
// If it's Its_a_Bar, you will get a runtime error.
let (Its_a_Foo foo) = myDU
f foo
回答2:
Rubber duck debugging case here...
I needed to write a function:
let MapToSubtype subtype =
match subtype with
| Foo foo -> foo
Then apply the function:
let x = MapToSubtype foo
... and worked like a charm.
Edit: Please note that as JustSomeFSharpGuy pointed out the MapToSubtype
function doesn't cover all cases so the compiler will give a warning and could give a runtime exception if something else than Foo
is passed in.
So the function should really look like this:
let MapToSubtype subtype =
match subtype with
| Foo foo -> foo
| _ -> // deal with exception here
来源:https://stackoverflow.com/questions/30841536/f-discriminated-union-downcasting-to-subtype