<div dir="ltr"><div dir="ltr"><div dir="ltr"><div>Hello everyone,</div><div>I'm a new haskeller, and (like many others, I assume) I thought  I'd try my hand</div><div>at Conway's "Game of Life".</div><div><br></div><div>So here is my code that seems to work (up to this point).</div><div>I am looking for feedback in order to improve my Haskell code on all levels.</div><div>Especially (In no particular order):</div><div>0- Find and fix bugs<br></div><div>1- Write more performance optimal code.</div><div>2- Good use of polymorphic types.</div><div>3- Good use of higher-order functions.</div><div>4- Good use of Haskell's common (and uncommon) abstractions.</div><div>5- Coding style (I'm finding it hard to let go of the function types :p)</div><div>6- Good code structuring allowing for reuse and updates.</div><div>7- Best options to give to the compiler.</div><div>8- Anything else that comes to your mind!<br></div><div><br></div><div>So I'd really appreciate your feedback :)<br></div><div><br></div><div>This is the wikipedia reference for the game of life: <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life</a></div><div><br></div><div>And this is the code:</div><div><br></div><div>START OF CODE<br></div><div>-- Game of life Haskell Implementation<br><br>import Data.List<br>import Control.Monad.State<br>import qualified Data.Map as M<br><br>-- The cell state is isomorphic to Bool.<br>type CellState = Bool<br><br>-- The coordinates of a cell<br>type Coord = (Int, Int)<br><br>-- The board size is (length, width)<br>type Size = (Int, Int)<br><br>-- The state of the board is simply the coordinates of its live cells<br>type Board = [Coord]<br><br>-- The state carried in the State Monad, used to count tags for cells<br>type TallyState = State (M.Map Coord (CellState, Int)) ()<br><br>-- The type of the game rules<br>type Rules = (Coord, CellState, Int) -> CellState<br><br>-- The type for the neighbor functions<br>type Neighbors = Coord -> [Coord]<br><br>-- Tally the live neighbors of live cells and relevant dead cells<br>tallyBoard :: Neighbors -> Board -> TallyState<br>tallyBoard nb = mapM_ $ tallyCoord nb<br><br>-- Tally a live cell: Set its state to True (alive) and tag its neighbors<br>-- This function takes the neighbors function as its first argument. We can use<br>-- different neighbor functions to change the zone of influence of a cell<br>tallyCoord :: Neighbors -> Coord -> TallyState<br>tallyCoord nb c = do<br>    let merge (a1,b1) (a2,b2) = (a1 || a2, b1 + b2)<br>    s <- get<br>    let s' = M.insertWith merge c (True, 0) s<br>    let neighbors = nb c<br>    put $ foldl' (\acc x -> M.insertWith merge x (False, 1) acc) s' neighbors<br><br>-- Extract the results from a TallyState<br>toResults :: TallyState -> [(Coord, CellState, Int)]<br>toResults s = map flatten . M.toList . execState s $ M.empty<br>    where flatten (x,(y,z)) = (x,y,z)<br><br>-- Use A Rules and Neighbors function to advance the board one step in time<br>advance :: Rules -> Neighbors -> Board -> Board<br>advance rules nb = map first . filter rules . toResults . tallyBoard nb<br>    where first (x,_,_) = x<br><br>-- The standard neighbors function<br>stdNeighbors :: Neighbors<br>stdNeighbors (x,y) =<br>    [ (a,b)<br>    | a <- [x-1, x, x+1]<br>    , b <- [y-1, y, y+1]<br>    , (a /= x) || (b /= y)<br>    ]<br><br>-- Standard game rules<br>stdRules :: Size -> Rules<br>stdRules (a,b) ((x,y),_,_)<br>    | (x < 0) || (y < 0) || (x >= a) || (y >= b) = False<br>stdRules _ (_,True,c)<br>    | (c == 2) || (c == 3) = True<br>    | otherwise = False<br>stdRules _ (_,False,3) = True<br>stdRules _ _ = False<br><br><br>-- Main loop<br>loop :: (Board -> Board) -> Board -> IO ()<br>loop f b = do<br>    print b<br>    unless (null b) $ loop f (f b)<br><br>-- Main function<br>main :: IO ()<br>main = do<br>    putStrLn "Choose board size (x,y)"<br>    input <- getLine<br>    putStrLn "Choose starting points"<br>    start <- getLine<br>    putStrLn "Game:"<br>    let size = read input<br>    let rules = stdRules size<br>    let initial = map read . words $ start<br>    let game = advance rules stdNeighbors<br>    loop game initial</div><div><br></div><div>END OF CODE</div><div><br></div><div>Thanks :)<br></div></div></div></div>