Learning Haskell – REPL

Because I am doing a lot of library work and I prefer text-based interfaces I always enjoy having a REPL (Read Evaluate Print Loop) next to my libraries for easy testing.

Here's my first Haskell REPL. This is actually quite nice, I think.

module Cli where import System.IO ( hSetBuffering , hSetEcho , hGetChar , hGetLine , hFlush , stdin , stdout , BufferMode(NoBuffering,LineBuffering) ) import Control.Exception ( Exception , throwIO , try ) import System.Console.ANSI ( getCursorPosition , setCursorPosition , clearScreen ) data Command = One | Two String | Clear | Cancel deriving (Eq, Show) data UnknownCommandException = UnknownCommand Char deriving Show instance Exception UnknownCommandException moveLeft :: IO () moveLeft = do position <- getCursorPosition case position of Just (x, y) -> if y > 0 then setCursorPosition x (y - 1) else return() _ -> return () getCommand :: String -> IO Command getCommand prompt = do hSetBuffering stdin NoBuffering hSetEcho stdin False putStr $ prompt <> ": " hFlush stdout input <- hGetChar stdin case input of 'o' -> return One 't' -> readTwo '0' -> return Clear c | c == 'c' || c == 'q' -> return Cancel _ -> throwIO (UnknownCommand input) readTwo :: IO Command readTwo = do moveLeft putStr "Enter text to reverse: " hFlush stdout hSetBuffering stdin LineBuffering hSetEcho stdin True input <- hGetLine stdin return $ Two input handleCommand :: Command -> IO Bool handleCommand Cancel = putChar '\n' >> return False handleCommand (Two str) = do putStrLn $ reverse str return True handleCommand Clear = do clearScreen setCursorPosition 0 0 return True handleCommand cmd = do putStrLn $ show cmd return True handleUnknownCommandException :: IO Bool -> IO Bool handleUnknownCommandException ioAction = do result <- try $ ioAction :: IO (Either UnknownCommandException Bool) case result of Left err -> print (err :: UnknownCommandException) >> return True Right continue -> return continue loop :: IO Bool -> IO () loop ioAction = ioAction >>= \continue -> if continue then loop ioAction else return () repl :: IO () repl = loop $ handleUnknownCommandException $ getCommand "$" >>= handleCommand

This is not necessarily the way you would want to do it. I wanted getCommand to throw an exception and I wanted a clear separation between the elements that form the REPL. I have just started learning Haskell so suggestions are very welcome.