How to extend F# discriminated unions for use from C#

Some time ago, I wrote about calling C# byref methods from F#. This time, I wanted to do things in reverse – the motivation being to provide a neat way of working with F# discriminated unions from C#.

Now, some support comes for free when you define a discriminated union in F#. For example, given this union type:

type Vehicle
| Car of int // number of doors
| Lorry of double // weight in tonnes

then from C#, you could query the union case in turn like this:

if (vehicle.IsCar)
{
    var doors = ((Vehicle.Car)vehicle).Item;
    driveCar( doors );
}
else if (vehicle.IsLorry)
{
    var weight = ((Vehicle.Lorry)vehicle).Item;
    driveLorry( weight );
}

However, although the provided “Is” methods are neat, the cast and the requirement to call “.Item” are rather ugly. What you really want is the ability to get the value of the union case without casting. That’s achievable if you add these methods to the original union definition by hand:

using System.Runtime.InteropServices // for [<Out>]
type Vehicle
| Car of int // number of doors
| Lorry of double // weight in tonnes
with
    member this.TryGetCar( [<Out>] result : int byref ) =
        match this with
        | Car doors ->
            result <- doors
            true
        | _ -> false
    member this.TryGetLorry( [<Out>] result : double byref ) =
        match this with
        | Lorry weight ->
            result <- weight
            true
        | _ -> false

Now the experience is much better from C#:

int doors;
double weight;
if ( vehicle.TryGetCar( out doors ) )
{
    driveCar( doors );
}
else if ( vehicle.TryGetLorry( out weight ) )
{
    driveLorry( weight );
}

and the new methods are usable from F# as well, although you’d probably prefer to use match:

// Use TryGet approach - returns bool, int
let isCar, doors = vehicle.TryGetCar()

// Use match approach - more natural for F#
match vehicle with
| Car doors -> driveCar(doors)
| Lorry weight -> driveLorry(weight)

Leave a comment

Filed under F#, Programming

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.