
I've rewritten it closer to the way I would have written it. Most of the changes are minor syntactic changes that you may or may not agree with. Probably the most confusing part of the original code was the declaration of the Node datatype as a (sum of) record(s), then the subsequent ignoring of the fact that it is a record. In my experience, most Haskell programmer's would not (for better or worse) use a record in this case. The comments I've added are with regards to the original source code; compare this version and yours to see the differences. As far as how the code actually works, it seems to be the most natural implementation. -- Pangolins in Haskell -- A simple animal guessing game module Main ( main ) where import Control.Monad ( liftM ) -- declared as record, but not used as record data Node = Guess String | Question String Node Node -- putStrLn s == putStr s >> putStr "\n" ask question = do putStrLn question getLine -- m >>= return == m (one of the monad laws) isYes "y" = True isYes "yes" = True isYes "Y" = True isYes "YES" = True isYes _ = False withArticle fullstring@(c:cs) | c `elem` "aeiou" = "an " ++ fullstring | otherwise = "a" ++ fullstring rewrite (Guess name) = do reply <- ask ("Is it " ++ withArticle name ++ "?") -- superfluous parens if isYes reply -- superfluous parens then do putStr "Computer wins!\n" return (Guess name) else do newName <- ask "What is it, then?" newQuestion <- newQuestion name newName return newQuestion rewrite (Question text ifYes ifNo) = do reply <- ask text if isYes reply then liftM (flip (Question text) ifNo) (rewrite ifYes) else liftM (Question text ifYes) (rewrite ifNo) newQuestion a1 a2 = do -- superfluous parens text <- ask ("Tell me a question that can be used to distinguish between " ++ withArticle a1 ++ " and " ++ withArticle a2 ++ ".") answer <- ask ("What would the answer to your question be if it was " ++ withArticle a1 ++ "?") if isYes answer -- superfluous parens then return (Question text (Guess a1) (Guess a2)) else return (Question text (Guess a2) (Guess a1)) -- superfluous parens duck = Guess "duck" dog = Guess "dog" initialNode = Question "Does it quack?" duck dog -- superfluous 'do' main = play initialNode play :: Node -> IO () play node = do rewrittenNode <- rewrite node playAgain <- ask "Play again?" if not (isYes playAgain) then putStrLn "Goodbye for now." -- superfluous 'do' else play rewrittenNode -- superfluous 'do'