Simple text menu in Haskell
I would like to know开发者_运维百科 what is the best solution to create simple menu with functionality described below (pseudo code) just like I'm used to:
while (true) {
x = readLine();
case (x):
x == "1" then do sth1 function
x == "2" then do sth2 function
}
Or maybe any other ideas on how to make a menu not in the pattern described above?
There's a few cool packages for high level ways to construct command line systems in general:
- ui-command: A framework for friendly commandline programs
- haskeline: A command-line interface for user input, written in Haskell.
- HCL: High-level library for building command line interfaces.
I particularly like ui-command, as it is an entire framework for your command line tools: It will dispatch to handler functions that you provide for each command, and also provide command-specific help to the user.
The goal is a polished feeling, rather than a hackish feeling.
Something like
menu :: IO ()
menu = do
putStrLn . unlines $ map concatNums choices
choice <- getLine
case validate choice of
Just n -> execute . read $ choice
Nothing -> putStrLn "Please try again"
menu
where concatNums (i, (s, _)) = show i ++ ".) " ++ s
validate :: String -> Maybe Int
validate s = isValid (reads s)
where isValid [] = Nothing
isValid ((n, _):_)
| outOfBounds n = Nothing
| otherwise = Just n
outOfBounds n = (n < 1) || (n > length choices)
choices :: [(Int, (String, IO ()))]
choices = zip [1.. ] [
("DoSomething", foo)
, ("Quit", bar)
]
execute :: Int -> IO ()
execute n = doExec $ filter (\(i, _) -> i == n) choices
where doExec ((_, (_,f)):_) = f
foo = undefined
bar = undefined
You could probably split the enumerating in "choices" so you only have the descriptions and functions inside it, a little bit of separation, but this works. Evaluating the "menu" function will let you choose what to do!
Here's another example that is a little more menu-like, in that it reads single characters in reacts directly, without requiring the user to press enter.
import System.IO
import System.Exit
import Control.Monad
main = forever (printMenu >> readChoice >>= menuAction)
printMenu = putStr "\np)rint 'Hello, world!'\ne)xit\nyour choice: " >> hFlush stdout
readChoice = hSetBuffering stdin NoBuffering >> hSetEcho stdin False >> getChar
menuAction 'p' = putStrLn "\nHello, world!"
menuAction 'e' = exitSuccess
menuAction _ = hPutStrLn stderr "\nInvalid choice."
精彩评论