Static records with arbitrary types for keys

I’m modeling a chess board as a record of records so that the first level are files (A-H) and the second level are ranks in those files (1-8) which give me individual squares:

type Square = {...}

type FileSquares =
    { "1" :: Square
    , "2" :: Square
    , "3" :: Square
    , "4" :: Square
    , "5" :: Square
    , "6" :: Square
    , "7" :: Square
    , "8" :: Square
    }

type Board =
    { "a" :: FileSquares
    , "b" :: FileSquares
    , "c" :: FileSquares
    , "d" :: FileSquares
    , "e" :: FileSquares
    , "f" :: FileSquares
    , "g" :: FileSquares
    , "h" :: FileSquares
    }

This works, but ideally, since I’m also modeling files and ranks as separate types:

data File = A | B | C | D | E | F | G | H
data Rank = One | Two | Three | Four | Five | Six | Seven | Eight

It would be rather nice if I could use those types for indexing the records, so that I have something like:

type Square = {...}

type FileSquares =
    { One :: Square
    , Two :: Square
    , Three :: Square
    , Four :: Square
    , Five :: Square
    , Six :: Square
    , Seven :: Square
    , Eight :: Square
    }

type Board =
    { A :: FileSquares
    , B :: FileSquares
    , C :: FileSquares
    , D :: FileSquares
    , E :: FileSquares
    , F :: FileSquares
    , G :: FileSquares
    , H :: FileSquares
    }

And then whenever I have a File and Rank, I can statically look up or update the data for the board without converting between the record property name and the type constructor.

The closest solution I can think of is to seal off the board stuff in a separate module and have:

type Board = Map File (Map Rank Square)

This would work, but since the compiler doesn’t know which keys there are in the map, I would have to use unsafePartial fromJust in a lot of places.

Does anyone have a different idea?

Hi

first you could do Map { file :: File, rank :: Rank } Square too (saves you one nesting).

The most straight forward way I can think of do get the mapping would be to write a function:

index :: Board -> File -> Rank -> Square

yes it’s a bit of (straight forward) boiler-plate code but IMHO it not that bad.

1 Like

Combining the keys is a great idea. I decided to go with it and everything seems nicer to deal with. I also use unsafePartial at exactly one place and even that can be refactored if I decide to manually return empty data for missing squares. Thanks for the advice.

1 Like