General TicTacToe in Haskell
Do you use the '-Wall' options of GHC ? It is quite handy, especially when there are non-exhaustive pattern matches in your (non-exhaustive pattern matches are bad ;-) )

TicTacToe.hs:84:41: Warning:
    Defaulting the following constraint(s) to type ‘Integer’
      (Num b0) arising from the literal ‘2’ at TicTacToe.hs:84:41
      (Integral b0) arising from a use of ‘^’ at TicTacToe.hs:84:39
    In the second argument of ‘(^)’, namely ‘2’
    In the second argument of ‘(&gt;)’, namely ‘length xss ^ 2’
    In the second argument of ‘(||)’, namely ‘n &gt; length xss ^ 2’

TicTacToe.hs:96:58: Warning:
    Defaulting the following constraint(s) to type ‘Integer’
      (Num b0) arising from the literal ‘2’ at TicTacToe.hs:96:58
      (Integral b0) arising from a use of ‘^’ at TicTacToe.hs:96:56
    In the second argument of ‘(^)’, namely ‘2’
    In the first argument of ‘show’, namely ‘(length board ^ 2)’
    In the first argument of ‘(++)’, namely ‘show (length board ^ 2)’

TicTacToe.hs:37:11: Warning:
    Pattern match(es) are non-exhaustive
    In an equation for ‘winner’: Patterns not matched: []

TicTacToe.hs:62:1: Warning:
    Pattern match(es) are non-exhaustive
    In an equation for ‘toPlayer’: Patterns not matched: Empty
Linking dist/build/TicTacToe/TicTacToe ...


Note: you can also feed your source code into hlint. It will sometimes be able to find some smarter ways to write code (though it won’t give you anything special in your case).

Util module

Concerning the Util module, unless you want to distribute it apart from your project, it should not be placed in Data.Utils.

The nth function can be written using splitAt:

nth :: Int -&gt; (a -&gt; a) -&gt; [a] -&gt; [a]
nth i f l = case splitAt i l of
                (start, elm:end) -&gt; start ++ [f elm] ++ end
                _                -&gt; []


That way, just reading the code makes it clear the nth function applies a function to the nth element of a list.

TicTacToe

You still have problems with putStr and the buffering of ouput. You should put a hFlush stdout right after your putStr call (imported from System.IO). Here’s my terminal:

Turn: Player 1
1
Tile number (1-9): +-+-+-+
|X| | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+

Turn: Player 2
2
Tile number (1-9): +-+-+-+
|X|O| |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+


You have placed validateInput and askInput in the "Game logic functions" block though they obviously are UI related.

In validateInput:


"Only integers allowed" is a message that must be generated by the user interface since it converts a String to an Int (this has no direct link to the game and could be reused in other programs)
"Out of range" and "Already filled" are messages that must be generated by the game itself


Note: as the error messages themselves are related to the user interface, the functions should not return them in plain english. One way to do this is to create a type : data ValidateError = OutOfRange | AlreadyFilled | OnlyInteger deriving (Eq) (or a class of types for more advanced error handling). With this, your functions do not deal with translation problems or with representation (an error could also be represented by a sound, an animation or a graphic).

You have a mutual recursion between askInput and gameStep. askInput should not call gameStep. askInput should just do one thing: ask the player a value and return it, nothing else. askInput also decides which player will play. Again, it is not its role. Try to apply the Single responsibility principle.