
Maxime Henrion wrote:
Hello all,
I have found myself writing instances of Show for some types of mine, and I did so by defining the showsPrec function, for performance reasons. I ended up with code that I find quite inelegant. Here's an example:
data Move = Move { movePiece :: PieceType, moveFile :: Maybe File, moveTarget :: Square, moveIsCapture :: Bool --movePromotion :: Maybe PieceType } deriving (Eq)
instance Show Move where showsPrec _ Move { movePiece = p, moveFile = f, moveTarget = s, moveIsCapture = c } = (if p /= Pawn then shows p else id) . (maybe id shows f) . (if c then ('x':) else id) . shows s
I considered writing a conditional composiion combinator to avoid all the 'if foo then f else id' code. Something looking like this:
f .? True g = f . g f .? False g = f
I'm not sure this is the best approach though, and I would be happy to hear about your suggestions for improving the style of this code, or any other comment that you think is appropriate.
Thanks, Maxime
Well, since ((.) :: ShowS -> ShowS -> ShowS) is a Monoid, you can use Writer to create the result:
import Control.Monad import Control.Monad.Writer
type Writes = Writer ShowS ()
data PieceType = Pawn | Other deriving (Eq,Show) type File = Int type Square = Int
data Move = Move { movePiece :: PieceType, moveFile :: Maybe File, moveTarget :: Square, moveIsCapture :: Bool --movePromotion :: Maybe PieceType } deriving (Eq)
instance Show Move where showsPrec = showsPrec_Move
showsPrec_Move :: Int -> Move -> ShowS showsPrec_Move _ Move { movePiece = p , moveFile = f , moveTarget = s , moveIsCapture = c } = execWriter $ do when (p/=Pawn) (tell (shows p)) maybe (return ()) (tell . shows) f when c (tell ('x':)) tell (shows s)
testMove = Move Other (Just 6) 10 True
which gives
*Main> testMove Other6x10 *Main> testMove { movePiece=Pawn } 6x10 *Main> testMove { movePiece=Pawn, moveIsCapture=False } 610