Simple command line todo-list Introduction

I've written a simple command line todo-list in Haskell.

The full code can be found here. However, given people's time constraints, I have selected three verbose functions for review.

Function One

program :: StateT TodoList IO ()
program = do
    StateT $ \xs -&gt; do
        (choice,_) &lt;- runStateT requestChoice xs
        (_,xs') &lt;- runStateT (parseChoice choice) xs
        runStateT printList xs'
        runStateT program xs'

main = runStateT program []


Questions:


Am I repeating myself too much by extracting the monads using runStateT?
Is there a better way to structure the main program? 


Function Two

parseChoice :: Text -&gt; StateT TodoList IO ()
parseChoice choice
    | first == "add" = addItem $ TodoItem second third
    | first == "remove" = removeItem $ parseInt second
    | first == "save" = saveList second
    | first == "load" = loadList second
    | first == "quit" = StateT $ \_ -&gt; exitSuccess
    | otherwise = invalidChoice first
    where   args =  split (=='|') (toLower choice)
        first = head args
        second = if length args &gt; 1 then args !! 1 else ""
        third = if length args &gt; 2 then args !! 2 else ""


Questions:


Am I using too many guards?


Function Three

removeItem :: Maybe Int -&gt; StateT TodoList IO ()
removeItem mn = StateT remove
    where remove = \xs -&gt; case mn of
        Just n -&gt; if n &lt;= length xs - 1 
            then return ((),removeAt n xs)
            else do
                S.putStrLn "Index too large"
                return ((),xs)
        Nothing -&gt; do
            S.putStrLn "Invalid index entered"
            return ((),xs)


General Questions


Which parts are un-idiomatic?
Which parts are superfluous?
Should I decompose these longer functions into smaller functions that do less?
How else can I improve this code?
It seems like you may not quite grasp the interplay between do-notation and monad transformer stacks. Take a look at how I've rewritten program here to leverage the actual machinery of StateT. The version you wrote is needlessly verbose due to your manually plumbing the state around!

program :: StateT TodoList IO ()
program = do
    choice &lt;- requestChoice
    parseChoice choice
    printList
    program


parseChoice can be cleaned up by favoring pattern matching over guards. Whenever you see a wall of guards that depend only on Eq, consider pattern matching instead.

parseChoice :: Text -&gt; StateT TodoList IO ()
parseChoice choice =
    case split (== '|') (toLower choice) of
        ["add", date, message] -&gt; addItem $ TodoItem date message
        ["remove", index]      -&gt; removeItem $ parseInt index
        ["save", file]         -&gt; saveList file
        ["load", file]         -&gt; loadList file
        ["quit"]               -&gt; lift exitSuccess
        (invalid:_)            -&gt; invalidChoice invalid


I think you can probably guess what can change about removeItem after reading my other changes now, so before reading this next code block try rewriting it on your own.

removeItem :: Maybe Int -&gt; StateT TodoList IO ()
removeItem Nothing  = lift $ putStrLn "Invalid index entered"
removeItem (Just n) = modify (removeAt n)