Text-based menu to manipulate a database text file I am making a program that lets a user manipulate a database (a text file).

In the code I am posting, I show only 2 of the menu choices, namely "createdb" and "deletedb", and a few functions I made to desperately make the functions more compact. But my problem is that the pattern is similiar for all the other menu options. I ask the user to either enter the name of the database or "b" to return to the menu, and then check if the file exists.

Is there a way I can easily separate this to make my code more compact? I tried to do this part in the menu and have the choice functions be of type

FilePath -&gt; IO ()


But then my menu looked really terrible. Here is a small part of the code:

type Choice = (String, String, IO ())

choices :: [Choice]
choices =
    [("a", "create a database", createdb),
    ("b", "delete a database", deletedb),
    ("c", "insert an entry to a database", insert),
    ("d", "print a database", selectall),
    ("e", "select entries from a database", select),
    -- more similiar choices

menu :: IO ()
menu = do
    (mapM_ putStrLn . map showChoice) choices
    c &lt;- get "Enter the letter corresonding to the action of choice:"
    case filter ((== c) . fst3) choices of
    [] -&gt; back "Not a valid choice. Try again"
    (_, _, f) : _ -&gt; f


createdb :: IO ()
createdb = do
    n &lt;- maybeName
    if isNothing n then menu else do
    let name = fromJust n
    fp &lt;- maybeFile name
    if isJust fp
    then back $ "Error: \"" ++ name ++ "\" already exist." 
    else do
        cols &lt;- get "Enter unique column names in the form n1,n2,...,n (No spaces):"
        let spl = (splitOnComma . toLower') cols
        case filter (== True) (hasDuplicates spl : map (elem ' ') spl) of
            [] -&gt; writeFile (name ++ ".txt") (cols ++ "\n")
            _  -&gt; back "Error: Column names must be unique and have no spaces."

deletedb :: IO ()
deletedb = do
    n &lt;- maybeName
    if isNothing n then menu else do
        let name = fromJust n
        fp &lt;- maybeFile name
        if isJust fp
        then removeFile (fromJust fp) 
        else back $ "Error: Could not find " ++ name

maybeName :: IO (Maybe String)
maybeName = do
    input &lt;- get "Enter database name or 'b' to go back to the menu."
    return $ case input of
        "b" -&gt; Nothing
        _   -&gt; Just input 

maybeFile :: String -&gt; IO (Maybe FilePath)
maybeFile name = do
    let fn = name ++ ".txt"
    exists &lt;- doesFileExist fn
    return $ if exists then Just fn else Nothing

back :: String -&gt; IO ()
back msg = do
    putStrLn msg
    menu

get :: String -&gt; IO String
get msg = do
    putStrLn msg
    getLine
I'd do Maybe FilePath -&gt; IO ():

menu :: IO ()
menu = do
    (mapM_ putStrLn . map showChoice) choices
    c &lt;- get "Enter the letter corresonding to the action of choice:"
    case filter ((== c) . fst3) choices of
    [] -&gt; back "Not a valid choice. Try again"
    (_, _, f) : _ -&gt; maybeName &gt;&gt;= maybe menu (maybeFile &gt;=&gt; f)

createdb :: Maybe FilePath -&gt; IO ()
createdb (Just file) = back $ "Error: \"" ++ file ++ "\" already exists." 
createdb Nothing = do
  cols &lt;- get "Enter unique column names in the form n1,n2,...,n (No spaces):"
  let spl = splitOnComma $ toLower' cols
  if hasDuplicates spl || any (elem ' ') spl
    then back "Error: Column names must be unique and have no spaces."
    else writeFile (name ++ ".txt") (cols ++ "\n")

deletedb :: Maybe FilePath -&gt; IO ()
deletedb Nothing = back $ "Error: Could not find that database."
deletedb (Just file) = removeFile file