Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Discriminated union member methods

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
like image 604
Robert Sim Avatar asked Nov 25 '14 20:11

Robert Sim


People also ask

What is a discriminated union type?

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.

When to use discriminated union?

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.

Are the unions of f# discriminated?

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”).

What is a tagged union C++?

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).


2 Answers

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.

like image 127
Marc Sigrist Avatar answered Jan 05 '23 01:01

Marc Sigrist


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).

like image 26
Rodrick Chapman Avatar answered Jan 05 '23 00:01

Rodrick Chapman