TicTacToe in Haskell 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 -&gt; [[Marking]]
getWinSeqs grid = horizontal ++ vertical ++ [fDiag, bDiag]
  where horizontal = grid
        vertical = transpose grid
        fDiag = zipWith (\ i x -&gt; i !! (n - x - 1)) grid [0..]
        bDiag = zipWith (!!) grid [0..]

-- Check if a game has been won on a board.
isWin :: Game -&gt; Maybe Player
isWin (Game grid _)
    | isWin' X  = Just X
    | isWin' O  = Just O
    | otherwise = Nothing
    where
        isWin' :: Player -&gt; Bool
        isWin' player = any (all (== Just player)) $ getWinSeqs grid

-- Make the next move.
move :: Game -&gt; Position -&gt; 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
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 -&gt; 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 -&gt; Position -&gt; Game


I'd write

move :: Int -&gt; Int -&gt; Game -&gt; Maybe Game


Instead of throwing an error, return Nothing. Then you can compute all possible moves with

moves :: Game -&gt; [Game]
moves game = catMaybes [move x y game | x&lt;-[0..2], y&lt;-[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 &gt;&gt;=
              move 1 1 &gt;&gt;= -- first move in center
              move 2 1 &gt;&gt;=
              move 2 0 &gt;&gt;=
              move 0 2 &gt;&gt;= -- forced
              move 0 0     -- setting up the trap


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

move :: Int -&gt; Int -&gt; Game -&gt; Maybe Game
move i j (Game grid player) = case grid !! i !! j of
   Just _ -&gt; Nothing
   Nothing -&gt; 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.