repl-toolkit 0.2.0.0 → 0.3.0.0
raw patch · 4 files changed
+56/−46 lines, 4 filesdep +exceptionsdep +transformersPVP ok
version bump matches the API change (PVP)
Dependencies added: exceptions, transformers
API changes (from Hackage documentation)
+ System.REPL.Command: instance Exception ParamNumError
- System.REPL: ask :: (MonadIO m, MonadError SomeException m, Functor m) => Asker m a -> Maybe Text -> m a
+ System.REPL: ask :: (MonadIO m, MonadCatch m, Functor m) => Asker m a -> Maybe Text -> m a
- System.REPL: ask' :: (MonadIO m, MonadError SomeException m, Functor m) => Asker m a -> m a
+ System.REPL: ask' :: (MonadIO m, MonadCatch m, Functor m) => Asker m a -> m a
- System.REPL: askEither :: (MonadIO m, Functor m) => Asker m a -> Maybe Text -> m (Either AskFailure a)
+ System.REPL: askEither :: (MonadIO m, MonadCatch m, Functor m) => Asker m a -> Maybe Text -> m (Either AskFailure a)
- System.REPL: promptAbort :: (MonadIO m, ListLikeIO full item, ListLikeIO full' Char) => Char -> full -> m (Maybe full')
+ System.REPL: promptAbort :: (MonadIO m, ListLikeIO full item, ListLikeIO full' Char, MonadCatch m) => Char -> full -> m full'
- System.REPL: untilValid :: (MonadIO m, MonadError SomeException m, Functor m, Read a) => m a -> m a
+ System.REPL: untilValid :: (MonadIO m, MonadCatch m, Functor m, Read a) => m a -> m a
- System.REPL.Command: commandDispatch :: (MonadIO m, MonadError SomeException m, Functor m) => Text -> [Command m z] -> m z
+ System.REPL.Command: commandDispatch :: (MonadIO m, MonadCatch m, Functor m) => Text -> [Command m z] -> m z
- System.REPL.Command: makeCommand :: (MonadIO m, MonadError SomeException m, Functor m) => Text -> (Text -> Bool) -> Text -> (Text -> m a) -> Command m a
+ System.REPL.Command: makeCommand :: (MonadIO m, MonadCatch m, Functor m) => Text -> (Text -> Bool) -> Text -> (Text -> m a) -> Command m a
- System.REPL.Command: makeCommand1 :: (MonadIO m, MonadError SomeException m, Functor m, Read a) => Text -> (Text -> Bool) -> Text -> Asker m a -> (Text -> a -> m z) -> Command m z
+ System.REPL.Command: makeCommand1 :: (MonadIO m, MonadCatch m, Functor m, Read a) => Text -> (Text -> Bool) -> Text -> Asker m a -> (Text -> a -> m z) -> Command m z
- System.REPL.Command: makeCommand2 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, Read b) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> (Text -> a -> b -> m z) -> Command m z
+ System.REPL.Command: makeCommand2 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> (Text -> a -> b -> m z) -> Command m z
- System.REPL.Command: makeCommand3 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, Read b, Read c) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> Asker m c -> (Text -> a -> b -> c -> m z) -> Command m z
+ System.REPL.Command: makeCommand3 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b, Read c) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> Asker m c -> (Text -> a -> b -> c -> m z) -> Command m z
- System.REPL.Command: makeCommand4 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, Read b, Read c, Read d) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> Asker m c -> Asker m d -> (Text -> a -> b -> c -> d -> m z) -> Command m z
+ System.REPL.Command: makeCommand4 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b, Read c, Read d) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> Asker m c -> Asker m d -> (Text -> a -> b -> c -> d -> m z) -> Command m z
- System.REPL.Command: makeCommand5 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, Read b, Read c, Read d, Read e) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> Asker m c -> Asker m d -> Asker m e -> (Text -> a -> b -> c -> d -> e -> m z) -> Command m z
+ System.REPL.Command: makeCommand5 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b, Read c, Read d, Read e) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> Asker m c -> Asker m d -> Asker m e -> (Text -> a -> b -> c -> d -> e -> m z) -> Command m z
- System.REPL.Command: makeCommand6 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, Read b, Read c, Read d, Read e, Read f) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> Asker m c -> Asker m d -> Asker m e -> Asker m f -> (Text -> a -> b -> c -> d -> e -> f -> m z) -> Command m z
+ System.REPL.Command: makeCommand6 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b, Read c, Read d, Read e, Read f) => Text -> (Text -> Bool) -> Text -> Asker m a -> Asker m b -> Asker m c -> Asker m d -> Asker m e -> Asker m f -> (Text -> a -> b -> c -> d -> e -> f -> m z) -> Command m z
- System.REPL.Command: makeCommandN :: (MonadIO m, MonadError SomeException m, Functor m, Read a) => Text -> (Text -> Bool) -> Text -> [Asker m a] -> [Asker m a] -> (Text -> [a] -> m z) -> Command m z
+ System.REPL.Command: makeCommandN :: (MonadIO m, MonadCatch m, Functor m, Read a) => Text -> (Text -> Bool) -> Text -> [Asker m a] -> [Asker m a] -> (Text -> [a] -> m z) -> Command m z
Files
- System/REPL.hs +31/−24
- System/REPL/Command.hs +20/−20
- changelog.txt +1/−0
- repl-toolkit.cabal +4/−2
System/REPL.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE LambdaCase #-} -- |Functions to expedite the building of REPLs. @@ -66,8 +67,8 @@ import Prelude hiding (putStrLn, putStr, getLine, reverse) import Control.Arrow (right, (|||)) -import Control.Exception -import Control.Monad.Except +import Control.Monad.Catch +import Control.Monad.IO.Class (MonadIO(liftIO)) import Data.Char (isSpace) import Data.Functor.Monadic import Data.ListLike(ListLike(empty, cons, reverse)) @@ -103,19 +104,23 @@ -- (commonly @'\ESC'@). This function temporarily tries to set the buffering mode -- to NoBuffering via 'System.IO.hSetBuffering', which may not be supported. -- See the documentation of 'System.IO.hSetBuffering' for details. -promptAbort :: (MonadIO m, ListLikeIO full item, ListLikeIO full' Char) - => Char -> full -> m (Maybe full') -promptAbort abortChar s = liftIO (do putStr s - IO.hFlush IO.stdout - bufMode <- IO.hGetBuffering IO.stdin - IO.hSetBuffering IO.stdin IO.NoBuffering - input <- getUntil empty - IO.hSetBuffering IO.stdin bufMode - return $ input >$> reverse) +promptAbort :: (MonadIO m, ListLikeIO full item, ListLikeIO full' Char, + MonadCatch m) + => Char -> full -> m full' +promptAbort abortChar s = do + liftIO $ putStr s + liftIO $ IO.hFlush IO.stdout + bufMode <- liftIO $ IO.hGetBuffering IO.stdin + liftIO $ IO.hSetBuffering IO.stdin IO.NoBuffering + input <- getUntil empty + `catch` (\(e :: AskFailure) -> + liftIO (IO.hSetBuffering IO.stdin bufMode) >> throwM e) + liftIO $ IO.hSetBuffering IO.stdin bufMode + return $ reverse input where - getUntil acc = do c <- getChar - if c == abortChar then return Nothing - else if c == '\n' then return $ Just acc + getUntil acc = do c <- liftIO $ getChar + if c == abortChar then throwM AbortFailure + else if c == '\n' then return acc else getUntil (cons c acc) -- Askers @@ -282,34 +287,33 @@ -- Running askers -------------------------------------------------------------------------------- --- |Executes an Asker. If the process fails, an exception is thrown --- The canonical instance of @MonadError SomeException@ is the 'ExceptT' monad. -ask :: (MonadIO m, MonadError SomeException m, Functor m) +-- |Executes an Asker. If the process fails, an exception is thrown. +ask :: (MonadIO m, MonadCatch m, Functor m) => Asker m a -> Maybe Text -> m a -ask a v = askEither a v >>= either (throwError . SomeException) return +ask a v = askEither a v >>= either throwM return -- |See 'ask'. Always reads the input from stdin. -- -- @ -- ask' a = ask a Nothing -- @ -ask' :: (MonadIO m, MonadError SomeException m, Functor m) +ask' :: (MonadIO m, MonadCatch m, Functor m) => Asker m a -> m a ask' a = ask a Nothing -- |Executes an 'Asker'. If the Text argument is Nothing, the user is asked -- to enter a line on stdin. If it is @Just x@, @x@ is taken to be input. -askEither :: (MonadIO m, Functor m) +askEither :: (MonadIO m, MonadCatch m, Functor m) => Asker m a -> Maybe Text -> m (Either AskFailure a) askEither a = maybe getInput check where - getInput = promptAbort '\ESC' (askerPrompt a) - >>= maybe (return $ Left AbortFailure) check + getInput = (promptAbort '\ESC' (askerPrompt a) >>= check) + `catch` (return . Left) check inp = case askerParser a inp of Left err -> return $ Left $ TypeFailure err @@ -318,7 +322,10 @@ -- |Repeatedly executes an ask action until the user enters a valid value. -- Error messages are printed each time. -untilValid :: (MonadIO m, MonadError SomeException m, Functor m, Read a) +untilValid :: forall m a.(MonadIO m, MonadCatch m, Functor m, Read a) => m a -> m a -untilValid m = m `catchError` (\l -> liftIO (putStrLn (show l)) >> untilValid m) +untilValid m = m `catch` handler + where + handler :: AskFailure -> m a + handler l = liftIO (putStrLn $ show l) >> untilValid m
System/REPL/Command.hs view
@@ -54,9 +54,9 @@ import qualified Prelude as P import Control.Arrow (left) -import Control.Exception import Control.Monad -import Control.Monad.Except +import Control.Monad.Catch +import Control.Monad.IO.Class (MonadIO(liftIO)) import Control.Monad.Loops (unfoldrM) import Data.Char (isSpace) import Data.Functor.Monadic @@ -100,6 +100,8 @@ data ParamNumError = NoParams | ExactParams | TooManyParams deriving (Enum, Show, Eq, Read, Typeable, Ord) +instance Exception ParamNumError + -- |Prints information (the command name, description and, if given, -- the number of parameters) about a command to the console. commandInfo :: MonadIO m => Command m a -> m () @@ -172,10 +174,8 @@ c ++ " takes at most " ++ T.pack (show (fromPeano maxNum :: Integer)) ++ " parameters."] -- |Checks the number of parameters before executing a monadic function. --- For compatibility (with the IO monad, mainly), the nominal type --- of the thrown exception is 'SomeException', but only AskFailures will --- actually be thrown in this function (other IO exceptions may occur). -checkParams :: (MonadIO m, MonadError SomeException m, Functor m) +-- Only AskFailures (and IOExceptions) will be thrown in this function. +checkParams :: (MonadIO m, MonadThrow m, Functor m) => Text -- ^The command name. -> Text -- ^The raw input (including the command name). -> Int -- ^The minimal number of parameters, excluding the command's name. @@ -186,11 +186,11 @@ -- passed, this will be a 'ParamNumFailure'. checkParams n inp minNum maxNum m = case readArgs inp of - Left l -> throwError (SomeException $ ParamFailure l) + Left l -> throwM (ParamFailure l) Right r -> if natLength r > maxNum + 1 then - throwError $ SomeException $ ParamFailure - $ paramErr n r minNum maxNum (errKind $ natLength r) + throwM $ ParamFailure + $ paramErr n r minNum maxNum (errKind $ natLength r) else m r where errKind len = if minNum == 0 && 0 == maxNum then NoParams @@ -210,7 +210,7 @@ else x -- |Creates a command without parameters. -makeCommand :: (MonadIO m, MonadError SomeException m, +makeCommand :: (MonadIO m, MonadCatch m, Functor m) => Text -- ^Command name. -> (Text -> Bool) -- ^Command test. @@ -224,7 +224,7 @@ f li -- |Creates a command with one parameter. -makeCommand1 :: (MonadIO m, MonadError SomeException m, Functor m, Read a) +makeCommand1 :: (MonadIO m, MonadCatch m, Functor m, Read a) => Text -- ^Command name. -> (Text -> Bool) -- ^Command test. -> Text -- ^Command description @@ -239,7 +239,7 @@ f li x1 -- |Creates a command with two parameters. -makeCommand2 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, +makeCommand2 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b) => Text -- ^Command name. -> (Text -> Bool) -- ^Command test. @@ -257,7 +257,7 @@ f li x1 x2 -- |Creates a command with three parameters. -makeCommand3 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, +makeCommand3 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b, Read c) => Text -- ^Command name. -> (Text -> Bool) -- ^Command test. @@ -277,7 +277,7 @@ f li x1 x2 x3 -- |Creates a command with four parameters. -makeCommand4 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, +makeCommand4 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b, Read c, Read d) => Text -- ^Command name. -> (Text -> Bool) -- ^Command test. @@ -299,7 +299,7 @@ f li x1 x2 x3 x4 -- |Creates a command with five parameters. -makeCommand5 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, +makeCommand5 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b, Read c, Read d, Read e) => Text -- ^Command name. -> (Text -> Bool) -- ^Command test. @@ -323,7 +323,7 @@ f li x1 x2 x3 x4 x5 -- |Creates a command with four parameters. -makeCommand6 :: (MonadIO m, MonadError SomeException m, Functor m, Read a, +makeCommand6 :: (MonadIO m, MonadCatch m, Functor m, Read a, Read b, Read c, Read d, Read e, Read f) => Text -- ^Command name. -> (Text -> Bool) -- ^Command test. @@ -355,7 +355,7 @@ -- If the number of passed parameters exceeds -- @length necc + length opt@, or if any 'Asker' fails, -- the command returns an 'AskFailure'. -makeCommandN :: (MonadIO m, MonadError SomeException m, Functor m, Read a) +makeCommandN :: (MonadIO m, MonadCatch m, Functor m, Read a) => Text -- ^Command name. -> (Text -> Bool) -- ^Command test. -> Text -- ^Command description @@ -391,15 +391,15 @@ -- trying the out in sequence. The first command whose 'commandTest' -- returns True is executed. If none of the commands match, -- @NothingFoundFailure@ is thrown. -commandDispatch :: (MonadIO m, MonadError SomeException m, Functor m) +commandDispatch :: (MonadIO m, MonadCatch m, Functor m) => Text -- ^The user's input. -> [Command m z] -- ^The command library. -> m z commandDispatch input cs = case readArgs input of - Left l -> throwError (SomeException $ ParamFailure l) + Left l -> throwM (ParamFailure l) Right input' -> if noMatch input' - then throwError (SomeException NothingFoundFailure) + then throwM NothingFoundFailure else do runCommand (fromJust $ first input') input where noMatch = isNothing . first
+ changelog.txt view
@@ -0,0 +1,1 @@+0.3 Ditched MonadError in favour of MonadThrow. This should make the functions much easier to use.
repl-toolkit.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: repl-toolkit -version: 0.2.0.0 +version: 0.3.0.0 synopsis: Toolkit for quickly whipping up command-line interfaces. description: A simple toolkit for quickly whipping up REPLs, input validation and sets of commands included. homepage: https://github.com/ombocomp/repl-toolkit @@ -13,6 +13,7 @@ category: System, REPL build-type: Simple cabal-version: >=1.10 +extra-source-files: changelog.txt source-repository head type: git @@ -21,5 +22,6 @@ library exposed-modules: System.REPL, System.REPL.State, System.REPL.Command other-extensions: OverloadedStrings, DeriveDataTypeable, FlexibleContexts, LambdaCase - build-depends: base >=4.7 && <5, functor-monadic >=0.1, text >=1.1, ListLike >=4.1, mtl >=2.2, parsec >=3.1, numericpeano >= 0.1, listsafe >= 0.1, monad-loops >= 0.3 + build-depends: base >=4.7 && <5, functor-monadic >=0.1, text >=1.1, ListLike >=4.1, exceptions >=0.4, parsec >=3.1, numericpeano >= 0.1, listsafe >= 0.1, monad-loops >= 0.3, mtl >= 2.2, transformers >= 0.3 default-language: Haskell2010 +