Are there any ways which I could improve and simplify this code? Is my code idiomatic Haskell?

Is the use of a lens library to change an element of a two-dimensional array unnecessary? If so, how could I replace it with a more elegant and simpler solution?

Do I need to make use of more types in order to make my code more understandable?

As a Haskell beginner, I am still trying to get used to the functional programming way and would appreciate it if answerers would provide a detailed and easy-to-understand answer (as well as references to materials which I could look up to learn more).

-- Tic Tac Toe implementation in Haskell.

module TicTacToe (
newGame,
isWin,
move
) where

import Control.Lens
import Data.List
import Data.Maybe

-- Size of the grid
n :: Int
n = 3

data Player = X | O deriving (Show, Read, Eq)
type Marking = Maybe Player
type Position = (Int, Int)

type Grid = [[Marking]]

-- An empty grid of size n x n.
emptyGrid :: Grid
emptyGrid = replicate n $replicate n Nothing data Game = Game { board :: Grid, curTurn :: Player } deriving Show -- Initial game which board is of size n x n, -- starting with O as the first player. newGame :: Game newGame = Game { board = emptyGrid, curTurn = X } getWinSeqs :: Grid -> [[Marking]] getWinSeqs grid = horizontal ++ vertical ++ [fDiag, bDiag] where horizontal = grid vertical = transpose grid fDiag = zipWith (\ i x -> i !! (n - x - 1)) grid [0..] bDiag = zipWith (!!) grid [0..] -- Check if a game has been won on a board. isWin :: Game -> Maybe Player isWin (Game grid _) | isWin' X = Just X | isWin' O = Just O | otherwise = Nothing where isWin' :: Player -> Bool isWin' player = any (all (== Just player))$ getWinSeqs grid

-- Make the next move.
move :: Game -> Position -> Game
move (Game grid player) (i, j) = Game {
board = nextGrid,
curTurn = nextPlayer
} where
nextGrid =
if isNothing $grid !! i !! j then set (ix i . ix j) (Just player) grid else error "position is occupied" nextPlayer | player == X = O | otherwise = X  ## 1 Answer A haskell beginner using Control.Lens... Certainly better than my first program which happened to a tic tac toe solver, so I can compare. We all know Why functional programming matters, don't we? Make your imports more specific: import Control.Lens (set,ix) import Data.List (transpose) import Data.Maybe (catMaybes)  You will note I replaced your isNothing by pattern matching. You only use type Position = (Int, Int) once. I would not declare this one. instead of  fDiag = zipWith (\ i x -> i !! (n - x - 1)) grid [0..]  I recommend  fDiag = zipWith (!!) (reverse grid) [0..] -- or fDiag = zipWith (!!) grid [n-1,n-2..]  Why force isWin to take Game? Grid would suffice. I suggest you to write a program that uses this module, then you know better. OK, now for move :: Game -> Position -> Game  I'd write move :: Int -> Int -> Game -> Maybe Game  Instead of throwing an error, return Nothing. Then you can compute all possible moves with moves :: Game -> [Game] moves game = catMaybes [move x y game | x<-[0..2], y<-[0..2]]  and I put Game in the last position so you can compose functions like move 2 0 . move 1 1$ newGame. Actually, that won't work because move now encapsulates in Maybe. But now you can write:

  test = Just newGame >>=
move 1 1 >>= -- first move in center
move 2 1 >>=
move 2 0 >>=
move 0 2 >>= -- forced
move 0 0     -- setting up the trap


because Maybe is a monad, and >>= allows forward chaining instead of .! The modified move looks like this:

move :: Int -> Int -> Game -> Maybe Game
move i j (Game grid player) = case grid !! i !! j of
Just _ -> Nothing
Nothing -> Just \$ Game {
board = set (ix i . ix j) (Just player) grid,
curTurn = nextPlayer player
}

nextPlayer X = O
nextPlayer O = X


I also made nextPlayer a top level function, I'd export it, it looks useful.

• Tbf the code using Control.Lens was from a person in #haskell-beginners. Thanks for your review; I'll wait for other answers to flow in before awarding the bounty. Nov 6 '14 at 8:08
• I finally got the chance to work on my implementation and found your answer to be of great help. I'll award the bounty now. Thanks! Nov 8 '14 at 14:33