Something I've found a little odd, is that it seems tricky to represent a fixed size array / grid without resorting to libraries (fixed-length / fixed-vector.) And those libraries, from a glance, seem relatively clunky.
I want to do something similar to this Rust code (for a chess board):
const N: usize = 8;
struct Piece {}
struct Board {
data: [[Piece; N]; N]
}
What is the idiomatic way to do this in Haskell? And why does it seem so difficult to represent fixed sized arrays?
The ideal code would look something like:
data Board (n :: Natural) = Board (Array Piece n)
But I guess the problem there is that it would need n
additional parameters for the constructor, which I'd have to type out by hand?
CodePudding user response:
The grids package looked pretty good, but it's dependencies didn't seem to be set up correctly.
I'd forgotten about the vector-sized package, which seems to be exactly what I want. Unfortunately it stores the size at runtime but that's not a real issue.
This code seems to work nicely:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeApplications #-}
module Main where
import Data.Vector.Sized
import GHC.TypeLits
import Prelude hiding (replicate)
data Piece = Piece
deriving (Show)
newtype Board (n :: Natural) = Board (Vector n (Vector n Piece))
deriving (Show)
newBoard :: Board 8
newBoard = Board $ replicate @8 $ replicate @8 Piece
main :: IO ()
main = print $ newBoard
CodePudding user response:
If your N is fixed at compile time and small (here, it sounds like it's fixed to 8), and you don't mind a little bit of extra typing, you can simply define a new type that has exactly the number of fields you want, all of the same type. I expect this approach does not give good random-access time, or good update time, but it is very simple, obviously correct, and easy to implement yourself. Whether those are good tradeoffs for your situation, I couldn't say.
data Eight a = Eight a a a a a a a a
deriving (Show, Functor, Foldable, Traversable)
instance Applicative Eight where
pure x = Eight x x x x x x x x
Eight fa fb fc fd fe ff fg fh <*> Eight a b c d e f g h =
Eight (fa a) (fb b) (fc c) (fd d) (fe e) (ff f) (fg g) (fh f)
newtype SixtyFour a = SixtyFour (Eight (Eight a))
deriving (Show, Functor, Foldable, Traversable)
As you can imagine, it is easy, although again tedious, to provide accessors and updaters for particular indices.