Storing values in an N x N grid I am trying to write a program to store/display a grid of size N x N with cells containing either a 1 or a 0 in preparation for further computation:

module Board where

import Data.List as List

data CellState = One | Zero deriving (Eq, Ord)

data Cell = Cell {cellPos :: (Int, Int), cellState :: CellState} deriving (Eq, Ord)

type Board = [Cell]

instance Show CellState where
  show One = "1"
  show Zero = "0"

instance Show Cell where
  show (Cell c x) = show (c,x)

genPositions :: Int -&gt; [(Int, Int)]
genPositions x = [ (a,b) | a &lt;- [0..(x-1)], b &lt;- [0..(x-1)] ]

genCellState :: Int -&gt; CellState
genCellState 0 = Zero
genCellState 1 = One

newBoard :: Int -&gt; [Int] -&gt; Board
newBoard i [x] = [Cell (round $ sqrt(fromIntegral i), round $ sqrt(fromIntegral i)) (genCellState x)]
  where positions = genPositions $ round $ sqrt(fromIntegral i)
newBoard i (x : xs) = [Cell (positions!!(i - 1 - length xs)) (genCellState x)] ++ newBoard i xs
  where positions = genPositions $ round $ sqrt(fromIntegral i)


What improvements can I make to this, both in terms of good practises and performance? I don't like Board being a list of Cells. I posted this on Stack Overflow by accident (have since deleted it) and someone recommended changing type Board = [Cell] to newtype Board = Board { getBoard :: Array (Int,Int) CellState } but I'm not too familiar with arrays in Haskell so not sure how this would work exactly. From what I have read they apply a function to the elements in the range [Int..Int] and return a list of tuples with the input and output.
By defining r = round . sqrt . fromIntegral, newBoard fits on the screen. positions doesn't appear to be used in newboard's first case.

Half the code disappears if we interpret Cell as ((Int, Int), Int).

newBoard's first case can be pushed one recursion call deeper, mapping [] to [] instead of [x] to the current right hand side.

[_] ++ _ should be simplified as _ : _.

positions is only used once, therefore I inline it.

type Cell = ((Int, Int), Int) -- (position, state)

genPositions :: Int -&gt; [(Int, Int)]
genPositions x = [ (a,b) | a &lt;- [0..(x-1)], b &lt;- [0..(x-1)] ]

r = round . sqrt . fromIntegral

newBoard :: Int -&gt; [Int] -&gt; [Cell]
newBoard i [] = []
newBoard i (x : xs) = (genPositions (r i)!!(i - 1 - length xs), x) : newBoard i xs


Successive elements of the list returned by genPositions and xs are zipped together; zip captures this pattern. i is now not needed in its non-rooted form and I recommend changing the interface to take N as an argument instead. Non-square arguments can currently crash !! anyway. genPositions is only used once, therefore I inline it.

type Cell = ((Int, Int), Int) -- (position, state)

newBoard :: Int -&gt; [Int] -&gt; [Cell]
newBoard n = zip $ liftA2 (,) [0..n-1] [0..n-1]


For type Board = Array (Int, Int) Int, Data.Array allows newBoard n = listArray ((0,0),(n-1,n-1)).