diff --git a/example/Main.hs b/example/Main.hs
--- a/example/Main.hs
+++ b/example/Main.hs
@@ -33,10 +33,9 @@
 bar = param "bar" "<number of bars>" parseBars setBars >+ do
         basic
         frob
-            where setBars str = do
-                    let b = read str
+            where setBars int = do
                     bars <- gets bars
-                    modify $ \s -> s { bars = bars + b }
+                    modify $ \s -> s { bars = bars + int }
                     return NewLevel
 
 baz :: CommandsT StateM ()
@@ -55,8 +54,8 @@
          liftIO . putStrLn $ "frobbing " ++ show n ++ " bars"
          return NoAction
 
-parseBars :: Validator StateM
-parseBars = return . fmap show . (readMaybe :: String -> Maybe Int)
+parseBars :: Validator StateM Int
+parseBars = return . readMaybe
 
 main :: IO ()
 main = do
diff --git a/src/System/Console/StructuredCLI.hs b/src/System/Console/StructuredCLI.hs
--- a/src/System/Console/StructuredCLI.hs
+++ b/src/System/Console/StructuredCLI.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE CPP,
              ImplicitParams,
+             ExistentialQuantification,
              FlexibleContexts,
              FlexibleInstances,
              RecordWildCards,
@@ -132,7 +133,7 @@
 
 type StateM m = StateT (State m) m
 
-type Handler m  = String -> m Action
+type Handler m a = a -> m Action
 
 -- | An 'Action' is returned as the result of a command handler provided by the user and
 -- it instructs the CLI of any changes in the CLI state
@@ -150,28 +151,29 @@
 -- | The 'Node' type contains the internal representation of a command. Normally there is no
 -- need to be concerned with it other than perhaps passing it opaquely to any utility parsers
 -- (like 'labelParser' for example), when writing a custom parser
-data Node m = Node { getLabel    :: String,
-                     getHint     :: String,
-                     getBranches :: [Node m],
-                     runParser   :: Parser m,
-                     isEnabled   :: m Bool,
-                     handle      :: Handler m }
+data Node m = forall a . Node {
+                        getLabel    :: String,
+                        getHint     :: String,
+                        getBranches :: [Node m],
+                        runParser   :: Parser m a,
+                        isEnabled   :: m Bool,
+                        handle      :: Handler m a }
 
-type Parser m = Node m -> String -> m ParseResult
+type Parser m a = Node m -> String -> m (ParseResult a)
 
 -- | A 'Validator' is a function to which a parsed string is given in order to perform
 -- any checks for validity that may be applicable, or even transforming the argument if
 -- necessary. Note that the validator runs in the "user" monad
-type Validator m = String -> m (Maybe String)
+type Validator m a = String -> m (Maybe a)
 
 type ExceptionHandler m = CLIException -> m (Either CLIException ())
 
 -- | There is no need to concern oneself with the 'ParseResult' type unless one is writing
 -- a custom parser, which should actually be rarer than not.
-data ParseResult =
+data ParseResult a =
     Done {
       -- | Output string to be fed to the command action handler
-      getOutput :: String,
+      getOutput :: a,
       -- | Part of the string matched during parsing of a command
       getDoneMatched :: String,
       -- | Remaining input data
@@ -264,11 +266,11 @@
 instance (MonadIO m) => Default (Settings m) where
     def = Settings Nothing "" (return " > ") False defExceptionHandler
 
-instance (Monad m) => Default (Parser m) where
+instance (Monad m) => Default (Parser m String) where
     def = labelParser
 
-instance (Monad m) => Default (Validator m) where
-    def = return . pure . id
+instance (Monad m) => Default (Validator m String) where
+    def = return . pure
 
 type ParserT m = ExceptT CLIException (HL.InputT (StateM m))
 
@@ -285,7 +287,7 @@
 execCommandsT  = fmap snd . runCommandsT
 
 data SearchResult m = Completed { completedNode      :: Node m,
-                                  completedOutput    :: String,
+                                  completedAction    :: m Action,
                                   completedMatched   :: String,
                                   completedRemaining :: String }
                     | Incomplete { incompleteNode    :: Node m,
@@ -361,10 +363,10 @@
 -- | Build a command node that takes one parameter (delimited by space). The parsed parameter
 -- is fed to the validator monadic function (in the "user" monad) and the resulting string
 -- if any is fed in turn as an argument to the handler action (also in the "user" monad).
-param :: (Monad m) => String       -- ^ Command keyword
-                   -> String       -- ^ Help text for this command (including argument description)
-                   -> Validator m  -- ^ Monadic validator (in the "user" monad)
-                   -> Handler m    -- ^ Handling action. Takes the validator output as argument
+param :: (Monad m) => String         -- ^ Command keyword
+                   -> String         -- ^ Help text for this command (including argument description)
+                   -> Validator m a  -- ^ Monadic validator (in the "user" monad)
+                   -> Handler m a    -- ^ Handling action. Takes the validator output as argument
                    -> CommandsT m ()
 param label hint validator handler =
     param' label hint validator (return True) handler
@@ -372,22 +374,22 @@
 -- | A variation of 'param' that allows for "disabling" the command at runtime by
 -- running the given "enable" monadic action (as always in the "user" monad) to check
 -- if the command should be displayed as an option and/or accepted or not.
-param' :: (Monad m) => String       -- ^ Command keyword
-                    -> String       -- ^ Help text for this command (including argument description)
-                    -> Validator m  -- ^ Monadic validator (in the "user" monad)
-                    -> m Bool       -- ^ Enable action in the "user" monad
-                    -> Handler m    -- ^ Handling action. Takes the validator output as argument
+param' :: (Monad m) => String         -- ^ Command keyword
+                    -> String         -- ^ Help text for this command (including argument description)
+                    -> Validator m a  -- ^ Monadic validator (in the "user" monad)
+                    -> m Bool         -- ^ Enable action in the "user" monad
+                    -> Handler m a    -- ^ Handling action. Takes the validator output as argument
                     -> CommandsT m ()
 param' label hint validator enable handler = do
   custom label hint parser enable handler
          where parser = paramParser hint validator
 
 -- | Create a command using a custom parser, providing thus complete flexibility
-custom :: (Monad m) => String     -- ^ Command keyword
-                    -> String     -- ^ Help text for this command
-                    -> Parser m   -- ^ Custom parser (runs in the "user" monad)
-                    -> m Bool     -- ^ Enable action in the "user" monad
-                    -> Handler m  -- ^ Handling action. Takes the validator output as argument
+custom :: (Monad m) => String      -- ^ Command keyword
+                    -> String      -- ^ Help text for this command
+                    -> Parser m a  -- ^ Custom parser (runs in the "user" monad)
+                    -> m Bool      -- ^ Enable action in the "user" monad
+                    -> Handler m a -- ^ Handling action. Takes the validator output as argument
                     -> CommandsT m ()
 custom label hint parser enable handler = do
   let node = Node { getLabel    = label,
@@ -416,7 +418,7 @@
 
 -- | A utility parser that reads an input and parses a command label. It can be used as part of
 -- custom parsers to first read the command keyword before parsing any arguments etc.
-labelParser :: (Monad m) => Node m -> String -> m ParseResult
+labelParser :: (Monad m) => Node m -> String -> m (ParseResult String)
 labelParser Node{..} input = do
     case nextWord input of
       ("?", remaining) ->
@@ -432,7 +434,7 @@
 (-.-) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
 (-.-) = (.).(.)
 
-paramParser :: (Monad m) => String -> (String -> m (Maybe String)) -> Node m -> String -> m ParseResult
+paramParser :: Monad m => String -> (String -> m (Maybe a)) -> Node m -> String -> m (ParseResult a)
 paramParser hint validator = parseParam -.- labelParser
     where parseParam  = flip (>>=) parseParam'
           parseParam' (Done _ matched rest) =
@@ -444,8 +446,11 @@
                 (word, remaining) -> do
                   v <- validator word
                   return $ maybe (badArg rest) (\x -> Done x (matched ++ ' ':word) remaining) v
-          parseParam' result =
-              return result
+          parseParam' (Fail x y) =
+              return $ Fail x y
+          parseParam' (Partial x y) =
+              return $ Partial x y
+          parseParam' NoMatch = return NoMatch
           badArg = Fail hint
 
 nextWord :: String -> (String, String)
@@ -520,9 +525,10 @@
     _ ->
       return ()
 
-    where buildPrompt ns = intercalate " " . (: reverse ns) <$> getPrompt ?settings
+    where buildPrompt ns = (showStack ns ++) <$> getPrompt ?settings
           withLabels     = fmap fst <$> getStack
           restore stack  = liftStateM . modify $ \s -> s { stack = stack }
+          showStack      = intercalate " " . reverse
 
 getStack :: (Monad m) => ParserT m [Level m]
 getStack = liftStateM $ gets stack
@@ -565,7 +571,7 @@
   case result of
     [Completed{ completedNode=node@Node{..}, ..}] -> do
       push completedMatched node
-      action <- liftUserM $ handle completedOutput
+      action <- liftUserM completedAction
       process' completedRemaining node action
     _ ->
       if checkForHelp . dropWhile isSpace $ reverse input then do
@@ -621,11 +627,11 @@
               enabled <- lift isEnabled
               if enabled then do
                   result <- lift $ runParser node input
-                  debugM $ "ran " ++ getLabel ++ " parser on " ++ show input ++ ": " ++ show result
+                  debugM $ "ran " ++ getLabel ++ " parser on " ++ show input ++ ": "  -- ++ show result
                   case result of
                     Done output matched rest ->
                         return Completed { completedNode      = node,
-                                           completedOutput    = output,
+                                           completedAction    = handle output,
                                            completedMatched   = matched,
                                            completedRemaining = rest }
                     Fail msg rest ->
diff --git a/structured-cli.cabal b/structured-cli.cabal
--- a/structured-cli.cabal
+++ b/structured-cli.cabal
@@ -1,5 +1,5 @@
 name:                structured-cli
-version:             2.3.0.0
+version:             2.4.0.0
 synopsis:            Application library for building interactive console CLIs
 description:         This module provides the tools to build a complete "structured" CLI application, similar to those found in systems like Cisco IOS or console configuration utilities etc. It aims to be easy for implementors to use.
 homepage:            https://gitlab.com/codemonkeylabs/structured-cli#readme
