3

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)
Scott Nimrod
  • 11,206
  • 11
  • 54
  • 118
  • 6
    The answer to your question is yes – John Palmer Mar 06 '16 at 10:46
  • So I have to state each union case just to perform the same action? – Scott Nimrod Mar 06 '16 at 10:58
  • 2
    Something like `root.Neighbours |> fun t -> t.Neighbours |> ...` – John Palmer Mar 06 '16 at 11:00
  • 4
    This may be *feedback* to you to reconsider your design. Is eight similar cases the best way to approach this particular problem? – Mark Seemann Mar 06 '16 at 11:01
  • @Mark Seemann - I'm sure that any F# code that I write is not ideal. However, I do find value in stumbling over design decisions and finding a better alternative. – Scott Nimrod Mar 06 '16 at 11:05
  • 3
    Agreed, and I'm sorry if my comment was taken as anything other than an attempt at a helpful hint. @JohnPalmer is correct that, *if* your design dictates those eight cases, then you'll need to pattern match on them. I understand if that seems like boilerplate (I think so too), but what I was trying to say, then, is that perhaps there's an alternative design that makes this issue moot. – Mark Seemann Mar 06 '16 at 11:11
  • Can you explain `letThereBeLife()` It does not make sense to me. Also I agree that the answer is yes, but am curious to see where you are going with this variation this classic. – Guy Coder Mar 06 '16 at 12:24
  • @Guy Coder - letThereBeLife is the function that I want to serve as the BigBang for the initial cells (i.e. first nine) to get started. I will add a "tick" function to serve as a time-slice for executing rules per cell based on its counterparts (i.e. neighbors). – Scott Nimrod Mar 06 '16 at 13:50
  • I was hoping that you would give a more detailed answer in the explanation and then see a different insight into your problem. Often when I have to write out the reason something works in detail, I see the answer I was seeking. For every question I ask at SO I probably delete 5 to 10 more before they are done. It is the research and cross checking of the details in writing the question that usually leads to the insight. – Guy Coder Mar 06 '16 at 14:04
  • @Guy Coder - I just don't have the functional mindset. I embrace learning by pain. So far, I'm just unable to think outside the box. I do welcome more hints. – Scott Nimrod Mar 06 '16 at 14:12
  • The part of your code that has me confused is that a cell has 8 neighbors and I am expecting the neighbors to be coded with a relative (x,y) location and don't see that. Also `Neighbor` starts to capture this idea, but then `Neighbors` is an unbounded data structure (list), why is it unbounded. Also I don't see a limit to the number of cells. I take it that this is not a final version so that would explain some of the questions. This is just food for thought, I don't expect an answer. – Guy Coder Mar 06 '16 at 14:34
  • As a suggestion to learn how to think functionally would be at the start to stay away from problems that have state. For me state leads to thinking objects or passing the state as parameter(s) or monads. While the good F# books have to bought, remember that F# started as a port of OCaml and OCaml is based on ML. There are many of those books for free and can be translated to F#, however they tend to use functors so either learn how to translate functors to F#, which I still have problems with, or avoid them for latter. – Guy Coder Mar 06 '16 at 14:45
  • @Guy Coder - I was considering just returning a new grid per time slice. That was my alternative to mutating cells. Instead I just duplicate them with an alternative state. – Scott Nimrod Mar 06 '16 at 14:52
  • I almost wrote another question for SO, but upon researching the details for the question I found the answer. If you want to know the problem, `let I x = x;;`,`let K x y = x;;`,`let S f g x = (f x) (g x);;` and then `let I' = S K K;;` should work but doesn't. How does one get `let I' = S K K;;` to work for `I' 3 = 3;;` and `I' true = true;;`? I actually read the answer sooner, but added to much when modifying the code, after writing all the bad ways in the question I saw my mistake and then it worked. – Guy Coder Mar 08 '16 at 19:41
  • @ Guy Coder - Yea... I find that as I'm detailing my question, I'm usually asking the wrong question as I narrow in on the original intent. Thanks for sharing. – Scott Nimrod Mar 08 '16 at 19:45

1 Answers1

4

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.

scrwtp
  • 13,437
  • 2
  • 26
  • 30