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
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] ->
...