I want to define a method shared by all members of a discriminated union. Currently I've implemented it like this, but it seems really inelegant- surely there is a better way. Suggestions?
type A =
{AData:string}
member this.SharedMethod (x:float) : int= ...
type B =
{BData:float}
member this.SharedMethod (x:float) : int= ...
type AB =
| A of A
| B of B
let CallSharedMethod (ab:AB) x =
match ab with
| AB.A(a') -> a'.SharedMethod x
| AB.B(b') -> b'.SharedMethod x
Discriminated union is a data structure used to hold a value that could take on different, fixed types. These are basically union types with a tag. To convert a union type into a discriminated union type, we use a common property across our types.
Discriminated unions are useful for heterogeneous data; data that can have special cases, including valid and error cases; data that varies in type from one instance to another; and as an alternative for small object hierarchies.
In this case the new type is the “sum” of the integer type plus the boolean type. In F#, a sum type is called a “discriminated union” type. Each component type (called a union case) must be tagged with a label (called a case identifier or tag) so that they can be told apart (“discriminated”).
Simply put, tagged unions are unions that have associated with them a piece of data that tracks which of the potential union properties is currently set. In C++ you can choose between structs and classes to encapsulate the union, or develop your own custom data structure base solution (like a map).
In your example, you have augmented the record types A
and B
each with an instance member. However, you can not only augment record types, you can also augment union types (as shown in @Rodrick's answer). If you do this, the union's augmentation will be "shared" by each DU case, which is what you have asked for. To make this more explicit, I have renamed some parts of your example:
type A = { AData:string }
type B = { BData:float }
type AB =
| ACase of A
| BCase of B
member __.SharedMethod x = 0
let callSharedMethod (ab:AB) x = ab.SharedMethod x
If you look at the compiled code in the object browser (or a decompiler like ILSpy), you will see that AB
is compiled as the base class of two subclasses AB.ACase
and AB.BCase
, and SharedMethod
belongs to the base class AB
.
See also section 8.5.1 of the F# 3.0 specification: Members in Union Types.
What about something like this?
type AB =
| A of string
| B of float
member self.SharedMethod (x : float) =
match self with
| A s -> x
| B f -> f + x
This assumes that you want each variant of your sum type (aka discriminated union) to do something different with the float parameter.
For the case of A
, I just return the original value since there's not much else I can do (since there is no generally useful relationship between string
and float
that yields a float
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With