As a beginner, I tried to implement TicTacToe in Haskell. How can I improve my code?
I have refactored my code: General TicTacToe in Haskell
module Data.List.Utils where import Data.List (intersperse) surround :: a -> [a] -> [a] surround x ys = x : intersperse x ys ++ [x] nth :: Int -> (a -> a) -> [a] -> [a] nth _ _  =  nth 0 f (x:xs) = f x : xs nth n f (x:xs) = x : nth (n - 1) f xs
module TicTacToe where import Data.List (transpose) import Data.Foldable (asum) import Data.List.Utils (nth, surround) data Tile = Empty | O | X deriving (Show, Eq) showTile :: Tile -> String showTile Empty = " " showTile O = "O" showTile X = "X" type Board = [[ Tile ]] showBoard :: Board -> String showBoard = unlines . surround vert . map (concat . surround horiz . map showTile) where vert = "+-+-+-+" horiz = "|" whoWon :: Board -> Maybe Tile whoWon xs = asum . map winner $ diag : anti : rows ++ cols where rows = xs cols = transpose xs diag = zipWith (!!) xs [0..] anti = zipWith (!!) xs [2,1..] winner [O, O, O] = Just O winner [X, X, X] = Just X winner _ = Nothing fillTile :: Int -> Int -> Tile -> Board -> Board fillTile row col = nth row . nth col . const isOver :: Board -> Bool isOver = all (all (/= Empty)) overMsg :: Maybe Tile -> String overMsg (Just x) = "Winner: " ++ (showTile x) overMsg Nothing = "Game Over! No winner." inRange :: Int -> Bool inRange x = x >= 0 && x <= 2 gameLoop :: Tile -> Board -> IO () gameLoop player board | isOver board || winner /= Nothing = putStrLn $ overMsg winner | otherwise = do putStrLn $ showBoard board putStrLn $ "Now player " ++ showTile player putStr "Row: " row <- fmap read getLine putStr "Col: " col <- fmap read getLine if inRange row && inRange col && (board !! row !! col == Empty) then gameLoop (if player == O then X else O) $ fillTile row col player board else do putStrLn "Invalid position, please try again." gameLoop player board where winner = whoWon board startBoard :: Board startBoard = replicate 3 (replicate 3 Empty) main = gameLoop O startBoard