Search code examples
typeselmunion-types

"Nested" union types in elm


I'm learning elm and trying to switch my mindset from TypeScript's type system. I was wondering what's the best way to use nested types like this:

type Player = X | O
type Cell = Player | Empty

viewCell: Cell -> string
viewCell cell = 
  case cell of
    X -> "X"
    O -> "O"
    Empty -> " "

the compiler complains

The first pattern is trying to match `X` values of type:

    Player

But the expression between `case` and `of` is:

    Cell

I can change viewCell like so, but then I don't know how to get the player

viewCell: Cell -> String
viewCell cell = 
  case cell of
    Player -> -- how to get the player ??
    Empty -> " "

The problem is not displaying the values per se, rather "destructuring" the nested union type so to speak. I want to use it later on in something like this:

check: (List Cell) -> string
check three =
  case three of
    [X, X, X] -> "X won"
    [O, O, O] -> "O won"
    _ -> "still going"

which also gives me similar complaints from the compiler


Solution

  • The only time a constructor and type can share a name is if you do something like:

    type Tag = Tag String

    Consider that you've said

    type Cell = Player | Empty

    but might also want

    type Winner = Player | None

    So what's Player? Is it a Cell or a Winner? It can't be both.

    The simple solution is:

    type Cell = PlayerCell Player | Empty

    type Winner = WinningPlayer Player | None

    PlayerCell X is a cell with player X. WinningPlayer O is a winner.

    When destructuring, you can nest:

    case cell of
      PlayerCell X -> 
        ...
      PlayerCell O -> 
        ...
      Empty -> 
        ...
    

    In fact, you can destructure more complex data structures:

      case cellRow of 
        [ PlayerCell O, PlayerCell O, PlayerCell O] -> 
          ...