Search code examples
f#conways-game-of-life

How do I iterate over a list of union cases and access each case's data?


How do I iterate over a list of union cases and access each case's data?

I have this line:

root.Neighbors |> Seq.filter(fun x -> print x)

However, Neighbors is a list:

Neighbors=[ One   { State=Survives; Neighbors=[] }
            Two   { State=Survives; Neighbors=[] }
            Three { State=Survives; Neighbors=[] }
            Four  { State=Survives; Neighbors=[] }
            Six   { State=Survives; Neighbors=[] }
            Seven { State=Survives; Neighbors=[] }
            Eight { State=Survives; Neighbors=[] }
            Nine  { State=Survives; Neighbors=[] } ]

I need to access the state of each neighbor within the list of neighbors.

However, I receive the following error:

The type 'Neighbor' does not match the type 'Cell' Type mismatch. Expecting a Neighbor -> unit but given a Cell -> unit

NOTE: Do I really need to do pattern matching just to access all of the neighbors?

Code:

module GameOfLife

type Neighbor = | One   of Cell
                | Two   of Cell
                | Three of Cell
                | Four  of Cell

                | Six   of Cell
                | Seven of Cell
                | Eight of Cell
                | Nine  of Cell

and CauseOfDeath = | Underpopulated // Fewer than 2 live neighbors
                   | Overpopulated  // More than 3 live neighbors

and State = | Dies of CauseOfDeath  
            | Survives    // 2 or 3 live neighbors
            | Resurected  // Is dead and has 3 live neighbors

and Neighbors = Neighbor List

and Cell = { State:State; Neighbors:Neighbors }

let letThereBeLife() = 
    { State=Survives; Neighbors=[ One   { State=Survives; Neighbors=[] }
                                  Two   { State=Survives; Neighbors=[] }
                                  Three { State=Survives; Neighbors=[] }
                                  Four  { State=Survives; Neighbors=[] }
                                  Six   { State=Survives; Neighbors=[] }
                                  Seven { State=Survives; Neighbors=[] }
                                  Eight { State=Survives; Neighbors=[] }
                                  Nine  { State=Survives; Neighbors=[] } ] }


let print (cell:Cell) =
    printf "This cell %A\n%A" cell.State cell.Neighbors

let tick root =
    root.Neighbors |> Seq.iter(print)

Solution

  • When you have a complex disriminated union with cases that share some common state between the cases, and you want to hide it behind a simpler interface, one technique would be to have a member that would hide the pattern matching boilerplate.

    type Neighbor =
        | One of Cell
        ...
        | Nine of Cell 
        static member Cell (n: Neighbor) = 
            match n with
            | One cell
            ...
            | Nine cell -> cell
    
     root.Neighbors |> Seq.iter (Neighbor.Cell >> print)
    

    As others mentioned in the comments however, you should really rethink your design. This huge Neighbor type feels like you discriminate over a wrong dimension.