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