2
\$\begingroup\$

I am currently learning Haskell and as an exercise, I am building a simple tic-tac-toe program. I start with

import Data.Vector

data Cell = Empty | X | O
type Board = Vector Cell

and then, to render the board:

render :: Board -> IO ()
render b = do
    renderSep
    renderRow 0 b
    renderSep
    renderRow 1 b
    renderSep
    renderRow 2 b
    renderSep
  where
    renderSep :: IO ()
    renderSep = putStrLn "+---+---+---+"
    renderRow :: Int -> Board -> IO ()
    renderRow r b = do
        putStr "| " >> putStr (cellRepr $ getCell r 0 b)
        putStr " | " >> putStr (cellRepr $ getCell r 1 b)
        putStr " | " >> putStr (cellRepr $ getCell r 2 b)
        putStrLn " |"

How could I abstract my implementation so that there's less repetition? Especially in the main body of the render function. I thought of using sequence together with the list fmap (\x -> renderRow x b >> renderSep) [0, 1, 2]. However, in this case I get a value of type IO [()] and not IO ().

\$\endgroup\$
6
  • \$\begingroup\$ Have you tried writing functions? \$\endgroup\$ – Mast Sep 18 '20 at 9:11
  • \$\begingroup\$ I'm sorry, I don't see what you mean. I wrote functions, yes. \$\endgroup\$ – Marc-André Brochu Sep 18 '20 at 9:13
  • 1
    \$\begingroup\$ To avoid the repetition. \$\endgroup\$ – Mast Sep 18 '20 at 9:16
  • \$\begingroup\$ I don't know haskell, but if you can write loops in the language that should take care of some of the code repetition. \$\endgroup\$ – pacmaninbw Sep 18 '20 at 23:31
  • 1
    \$\begingroup\$ A possible improvement is to use forM_ – notice the underscore, which is a convention and means that we discard the results. Using this function you can replace the repetitive part with forM_ [0..2] $ \i -> renderRow i b >> renderSep. \$\endgroup\$ – Tsvetan Ovedenski Sep 27 '20 at 7:22
1
\$\begingroup\$

I think a simple way to address the problem is by building the board first then printing it:

empty_row :: String
empty_row = "+---+---+---+"

board_repr :: type of b -> String
board_repr b = empty_row + concat ( map (\x -> (build_row x b) + empty_row ) [0, 1, 2] )

main = print (board_repr b)

Take this as pseudo-code pointing in an interesting direction by separating the concerns of building a representation and printing it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.