Type-driven development is a methodology of modeling domain-specific data as types, for example a chess engine framework will need moves, sides (white/black), pieces, the board and piece coordinates.

By thinking about how we'll model this for a bit and starting with this step, we'll be able to have powerful but simple abstractions "fall out" of the design later that will make our life very easy.

Here's how I might start out:

```haskell
data Side = White | Black
derive instance Generic Side _
derive instance Eq Side

data Piece
  = Pawn
  | Knight
  | Bishop
  | Rook
  | Queen
  | King
derive instance Generic Side _
derive instance Eq Side

data File = A | B | C | D | E | F | G
derive instance Generic File _
derive instance Eq File
derive instance Ord File
instance Enum File where        -- <snip>
instance Bounded File where     -- <snip>
instance BoundedEnum File where -- <snip>

data Rank = R1 | R2 | R3 | R4 | R5 | R6 | R7 | R8
derive instance Generic Rank _
derive instance Eq Rank
derive instance Ord Rank
instance Enum Rank where        -- <snip>
instance Bounded Rank where     -- <snip>
instance BoundedEnum Rank where -- <snip>

data Pos = Pos File Rank
derive instance Generic Pos _
derive instance Eq Pos
derive instance Ord Pos

data MoveKind = To | Promotes Piece
derive instance Generic MoveKind _
derive instance Eq MoveKind

data Move = Move Pos MoveKind Pos
derive instance Generic Move _
derive instance Eq Move

data Board = Board (Map Pos (Side /\ Piece))
derive instance Generic Board _
derive instance Eq Board

data Game = Game (NonEmptyArray Board)
derive instance Generic Game _
```

The starting board would look something like:

```haskell
boardInit :: Board
boardInit =
  let
    pieces rank side =
      [ Pos A rank /\ side /\ Rook
      , Pos B rank /\ side /\ Knight
      , Pos C rank /\ side /\ Bishop
      , Pos D rank /\ side /\ Queen
      , Pos E rank /\ side /\ King
      , Pos F rank /\ side /\ Bishop
      , Pos G rank /\ side /\ Knight
      , Pos H rank /\ side /\ Rook
      ]
    pawns rank side =
      (upFromIncluding bottom :: Array File)
      <#> \file -> Pos file rank /\ side /\ Pawn
  in
    Board
      $ Map.fromFoldable
      $ pieces R1 White
      <> pawns R2 White
      <> pawns R7 Black
      <> pieces R8 Black

gameInit :: Game
gameInit = Game $ pure boardInit
```

> [!tip]- Performance Optimization
> [[Map]] is a B-tree map, meaning lookups use bifurcation in `O(log(n))` time. To do position lookups in constant time, we could replace Map with [[HashMap]], and add [[Hashable]] implementations to `Pos`.

> [!tip]- Enums for rank & file
> A temptation may be to use [[Int]] for encoding rank & file in positions, but this could quickly get hairy with legal bounds-checking.
> By encoding X & Y as enums `Rank` & `File`, we've made it impossible to put illegal positions into the `Board`.