@ Fyodor's answer explains why your current approach will not work.
One common way to achieve this in functional languages ββis to use lightning (not to be confused with zip or related functions).
The idea is that lightning is a representation of the data structure that focuses on a specific part (for example, a cell in a grid). You can apply transformations to the zipper to βmoveβ this focus around, and you can apply various transformations to query or βmutateβ the data structure relative to the focus. Both types of transformations are purely functional - they act on an immutable lightning and just create a new copy.
Here you can start with a zipper for an endless list with the info item:
data Zipper a = Zipper [a] a Int [a] deriving (Functor) -- Zipper ls xn rs represents the doubly-infinite list (reverse ls ++ -- [x] ++ rs) viewed at offset n instance (Show a) => Show (Zipper a) where show (Zipper ls xn rs) = show (reverse (take 3 ls)) ++ " " ++ show (x,n) ++ " " ++ show (take 3 rs)
This Zipper designed to represent double infinite (i.e. a list that is infinite in both directions). An example would be:
> Zipper [-10,-20..] 0 0 [10,20..] [-30,-20,-10] (0,0) [10,20,30]
Designed to represent a list of all (positive and negative) integer multiples of ten focused on the value 0 , position 0 and actually uses two infinite Haskell lists, one for each direction.
You can define functions to move the focus forward or backward:
back, forth :: Zipper a -> Zipper a back (Zipper (l:ls) xn rs) = Zipper ls l (n-1) (x:rs) forth (Zipper ls xn (r:rs)) = Zipper (x:ls) r (n+1) rs
so that:
> forth $ Zipper [-10,-20..] 0 0 [10,20..] [-20,-10,0] (10,1) [20,30,40] > back $ back $ Zipper [-10,-20..] 0 0 [10,20..] [-50,-40,-30] (-20,-2) [-10,0,10] >
Now a Grid can be represented as a zipper of rows, with each row a zipper of values:
newtype Grid a = Grid (Zipper (Zipper a)) deriving (Functor) instance Show a => Show (Grid a) where show (Grid (Zipper ls xn rs)) = unlines $ zipWith (\ab -> a ++ " " ++ b) (map show [n-3..n+3]) (map show (reverse (take 3 ls) ++ [x] ++ (take 3 rs)))
along with a set of focus functions:
up, down, right, left :: Grid a -> Grid a up (Grid g) = Grid (back g) down (Grid g) = Grid (forth g) left (Grid g) = Grid (fmap back g) right (Grid g) = Grid (fmap forth g)
You can define a getter and setter for the focused element:
set :: a -> Grid a -> Grid a set y (Grid (Zipper ls row n rs)) = (Grid (Zipper ls (set' row) n rs)) where set' (Zipper ls' xm rs') = Zipper ls' ym rs' get :: Grid a -> a get (Grid (Zipper _ (Zipper _ x _ _) _ _)) = x
and it may be convenient to add a function that moves the focus back to the origin for display:
recenter :: Grid a -> Grid a recenter g@ (Grid (Zipper _ (Zipper _ _ m _) n _)) | n > 0 = recenter (up g) | n < 0 = recenter (down g) | m > 0 = recenter (left g) | m < 0 = recenter (right g) | otherwise = g
Finally, using a function that creates an all- False grid:
falseGrid :: Grid Bool falseGrid = let falseRow = Zipper falses False 0 falses falses = repeat False falseRows = repeat falseRow in Grid (Zipper falseRows falseRow 0 falseRows)
you can do things like:
> let (&) = flip ($) > let testGrid = falseGrid & set True & right & set True & recenter > testGrid -3 [False,False,False] (False,0) [False,False,False] -2 [False,False,False] (False,0) [False,False,False] -1 [False,False,False] (False,0) [False,False,False] 0 [False,False,False] (True,0) [True,False,False] 1 [False,False,False] (False,0) [False,False,False] 2 [False,False,False] (False,0) [False,False,False] 3 [False,False,False] (False,0) [False,False,False] > testGrid & right & left & get True > testGrid & left & right & get True > testGrid & get True >
Full example:
{-