"Nested" elm 中的联合类型

"Nested" union types in elm

我正在学习 elm 并试图将我的思维方式从 TypeScript 的类型系统转变过来。我想知道像这样使用嵌套类型的最佳方法是什么:

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

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

编译器抱怨

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

    Player

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

    Cell

我可以像这样更改 viewCell,但我不知道如何获取播放器

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

问题不在于显示值本身,而是 "destructuring" 可以这么说的嵌套联合类型。我想稍后像这样使用它:

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

编译器也给我类似的抱怨

构造函数和类型唯一可以共享名称的情况是:

type Tag = Tag String

考虑一下你说过的

type Cell = Player | Empty

但可能还想要

type Winner = Player | None

那么 Player 是什么?是 Cell 还是 Winner?不能两者都是。

简单的解决方案是:

type Cell = PlayerCell Player | Empty

type Winner = WinningPlayer Player | None

PlayerCell X 是玩家 X 的单元格。WinningPlayer O 是赢家。

解构时可以嵌套:

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

事实上,你可以解构更复杂的数据结构:

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

type Cell = Player | Empty

Player不是类型,而是Cell类型的值。但是,您也可以给它一个参数,在这种情况下,它将是一个值构造函数,当给定一个参数 return 时,它是一个 Cell 类型的值。所以在

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

Player Player 中的第一个 Player 本质上是一个函数,当给定一个 Player 类型的值时,它将 return 一个 Cell 类型的值。或打字说 Player -> Cell

另请注意,类型和构造函数可以具有相同的名称,因为它们位于不同的域中。它们并不冲突,因为它们指的是不同的东西,一个指的是类型,另一个指的是值(构造函数)。但事实上你可以并不一定意味着你应该,因为它可能会非常混乱。

然后您可以在 Cell 和嵌套的 Player 上进行模式匹配,如下所示:

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

viewCell: Cell -> String
viewCell cell = 
  case cell of
    Player X -> "X"
    Player O -> "O"
    Empty -> " "
这里的

PlayerEmpty指的是Cell的constructors/variants,不是类型。同样,XO 指的是 Player 的变体,它们也不是类型。