«Вложенные» типы типов вязов - PullRequest
1 голос
/ 10 января 2020

Я изучаю вяз и пытаюсь переключить свое мышление с системы типов 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 -> " "

Проблема не в том, чтобы отобразить значения как таковые, а в том, чтобы «разрушить» вложенный тип объединения, так сказать. Я хочу использовать его позже как-то так:

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

, что также вызывает у меня аналогичные жалобы от компилятора

Ответы [ 2 ]

4 голосов
/ 10 января 2020

In

type Cell = Player | Empty

Player не тип, а значение типа Cell. Однако вы также можете указать ему аргумент, и в этом случае это будет конструктор значения, который при получении аргумента возвращает значение типа Cell. Таким образом, в

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

первый Player в Player Player по сути является функцией, которая при задании значения типа Player будет возвращать значение типа 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 -> " "

Player и Empty здесь относятся к конструкторам / вариантам Cell, а не к типам. И аналогично X и O относятся к вариантам Player, которые также не являются типами.

3 голосов
/ 10 января 2020

Единственный раз, когда конструктор и тип могут поделиться именем, это если вы сделаете что-то вроде:

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