Given a discriminated union case, can you get the next case?

Viewed 86

I am trying to write code for a robot simulation. For the direction, I have a DU:

type Direction = North | East | South | West

When the type is South and you give the instruction to turn twice to the right, is there an option that the value practically starts again at North?

2 Answers

What you have there is a discriminated union defining 4 cases. There's no automatic way to understand rotating the values but you can write your own functions to achieve this:

type Direction = North | East | South | West

module Direction = 
    let turnRight = 
        function  //The function keyword is a shortcut for (fun x -> match x with...)
        | North -> East
        | East -> South
        | South -> West
        | West -> North

//Usage
let direction = North
let direction2 = Direction.turnRight direction

Note: In F# it is typical to declare functions that work on a particular type in a module of the same name.

There is a way to get the next case automatically using reflection. Usually it's best to avoid reflection where possible, but this usage of it is quite constrained because it only runs once when you load the module, so it should be very easy to spot immediately if it causes an exception:

open FSharp.Reflection

let inline getUnionCases<'a> () =
    FSharpType.GetUnionCases typeof<'a>
    |> Array.map (fun case ->
        FSharpValue.MakeUnion(case,[||]) :?> 'a)

type Direction = North | East | South | West

let directions = getUnionCases<Direction>()

let rightDirections =
    directions
    |> Array.mapi (fun i d -> d, directions.[(i + 1) % directions.Length])
    |> Map

let turnRight d = rightDirections |> Map.find d

North |> turnRight // East
West |> turnRight // North
  • The reflection occurs when evaluating directions which is a module level value, so it happens once on module load (i.e. app startup). If you place this value inside a function or class then you will lose this safety.

  • The inline keyword is only needed for this to work in Fable (F# to JS compiler)

  • This only works if each DU case has no data. If you changed North to North of int this code will throw an exception.

Related