Safe Haskell | None |
---|---|
Language | Haskell2010 |
Asking the user for input on the console.
The main type is Asker
, which takes care of parsing
and verifying user input.
- type PromptMsg = Text
- type TypeError = SomeException
- type PredicateError = SomeException
- type Predicate m a b = a -> m (Either PredicateError b)
- type Predicate' m a = Predicate m a a
- type Parser a = Text -> Either TypeError a
- data Asker m a b = Asker {
- askerPrompt :: Text
- askerParser :: Parser a
- askerPredicate :: Predicate m a b
- type Asker' m a = Asker m a a
- data SomeREPLError = forall e . Exception e => SomeREPLError e
- data SomeAskerError = forall e . Exception e => SomeAskerError e
- data AskerTypeError = AskerTypeError SomeException
- data AskerPredicateError = AskerPredicateError SomeException
- data GenericTypeError = GenericTypeError Text
- data GenericPredicateError = GenericPredicateError Text
- genericTypeError :: Text -> SomeException
- genericPredicateError :: Text -> SomeException
- typeAskerP :: Applicative m => PromptMsg -> Parser a -> Asker' m a
- maybeAskerP :: Applicative m => PromptMsg -> Parser a -> Predicate m a b -> Asker m (Maybe a) (Maybe b)
- newtype Verbatim = Verbatim {
- fromVerbatim :: Text
- readParser :: Read a => (Text -> TypeError) -> Parser a
- asker :: (Functor m, Read a) => PromptMsg -> (Text -> TypeError) -> Predicate' m a -> Asker' m a
- lineAsker :: Applicative m => Asker' m Text
- typeAsker :: (Applicative m, Read a) => PromptMsg -> (Text -> TypeError) -> Asker' m a
- predAsker :: Functor m => PromptMsg -> Predicate m Text b -> Asker m Text b
- maybeAsker :: (Applicative m, Read a) => PromptMsg -> (Text -> TypeError) -> Predicate' m a -> Asker' m (Maybe a)
- ask :: (MonadIO m, MonadCatch m) => Asker m a b -> Maybe Text -> m b
- ask' :: (MonadIO m, MonadCatch m) => Asker m a b -> m b
- askEither :: (MonadIO m, MonadCatch m) => Asker m a b -> Maybe Text -> m (Either SomeAskerError b)
- untilValid :: forall m a. (MonadIO m, MonadCatch m, Read a) => m a -> m a
- boolPredicate :: Functor m => (a -> m Bool) -> (a -> PredicateError) -> Predicate' m a
- data PathRootDoesNotExist = PathRootDoesNotExist FilePath
- data PathIsNotWritable = PathIsNotWritable FilePath
- filepathAsker :: MonadIO m => PromptMsg -> (FilePath -> TypeError) -> Predicate m (PathExistenceType, FilePath) b -> Asker m FilePath b
- writablefilepathAsker :: MonadIO m => PromptMsg -> (FilePath -> TypeError) -> Predicate m (PathExistenceType, FilePath) b -> Asker m FilePath b
Types
type TypeError = SomeException Source
An error message indicating that a value wasn't able to be parsed.
type PredicateError = SomeException Source
An error message indicating that a value failied a predicate.
type Predicate m a b = a -> m (Either PredicateError b) Source
A predicate which a value has to fulfil.
type Predicate' m a = Predicate m a a Source
A predicate which does not change the type of its input.
type Parser a = Text -> Either TypeError a Source
A parser which either returns a parsed value or an error message.
The description of an 'ask for user input'-action.
The type parameters are the used monad (typically IO
or ExceptT
),
the type of the read value and the type of the error that is thrown
in case of failures.
The components are a prompt, a parser, and a predicate that the parsed value must fulfil. The the predicate
- is monadic and
- can change the returned type (useful for adjoining additional information)
Asker | |
|
type Asker' m a = Asker m a a Source
An Asker which does not convert its argument into different type after parsing.
Exceptions
data SomeAskerError Source
Generic error related to Asker
s. Either the input was incorrect
in some way, or the process was aborted by the user.
forall e . Exception e => SomeAskerError e |
data AskerTypeError Source
The input wasn't able to be parsed.
data AskerPredicateError Source
The parsed value failed a predicate.
data GenericTypeError Source
A generic type failure for use with Askers.
data GenericPredicateError Source
A generic predicate failure for use with Askers.
genericTypeError :: Text -> SomeException Source
Constructor for GenericTypeError
which wraps the value into a SomeException
.
genericPredicateError :: Text -> SomeException Source
Constructor for GenericTypeError
which wraps the value into a SomeException
.
Creating askers
These are all just convenience functions.
You can also create Asker
s directly via the constructor.
For errors, you can supply a custom exception or use GenericTypeError
,
GenericPredicateError
.
typeAskerP :: Applicative m => PromptMsg -> Parser a -> Asker' m a Source
Creates an Asker
which only cares about the type of the input.
maybeAskerP :: Applicative m => PromptMsg -> Parser a -> Predicate m a b -> Asker m (Maybe a) (Maybe b) Source
Creating askers via Read
These askers use readMaybe
as their parser.
It is possible to ask for Strings, but then quotes will be required
around them (per their Read-instance). To get the user's
input as-is, use the Verbatim
type or predAsker
.
A verbatim Text whose Read instance simply returns the read string, as-is. This is useful for askers which ask for strings without quotes.
readParser :: Read a => (Text -> TypeError) -> Parser a Source
A parser based on readMaybe
. This suffices for the parsing of
most data types.
asker :: (Functor m, Read a) => PromptMsg -> (Text -> TypeError) -> Predicate' m a -> Asker' m a Source
Creates a general Asker
with readMaybe
as its parser.
Using readMaybe
is perfectly fine for most values, keep in mind
that the input Text has to be unpacked into a string. This can be costly
on very large inputs.
NOTE: Instances of String/Text have to be surrounded with quotes (").
You practically never want this when asking for input.
If you want to get the user input as-is, restrict the return type to
Asker m Verbatim
or use 'predAsker'/'lineAsker'.
lineAsker :: Applicative m => Asker' m Text Source
A wrapper aroung getLine
. Prints no prompt and returns the user input as-is.
typeAsker :: (Applicative m, Read a) => PromptMsg -> (Text -> TypeError) -> Asker' m a Source
Creates an Asker
based on Read which just cares about the type of the input.
predAsker :: Functor m => PromptMsg -> Predicate m Text b -> Asker m Text b Source
Creates an Asker
which takes its input verbatim as Text
.
Quotes around the input are not required.
The input thus only has to pass a predicate, not any parsing.
maybeAsker :: (Applicative m, Read a) => PromptMsg -> (Text -> TypeError) -> Predicate' m a -> Asker' m (Maybe a) Source
An asker based on Read which asks for an optional value.
Running askers
Created askers can be run via these functions. Since the parsing depends on the Read-instance, the expected result type must be explicitly given. E.g.:
intAsker :: Asker IO Int intAsker = typeAsker "> " "Expected Int!"
or, for polymorphic askers,
genericAsk :: Read a => Asker IO a genericAsk = typeAsker "> " "Couldn't parse value!" ... do (x :: Int) <- genericAsk (y :: Int) <- genericAsk putStrLn $ "The sum is: " ++ show (x+y)
ask :: (MonadIO m, MonadCatch m) => Asker m a b -> Maybe Text -> m b Source
Executes an Asker. A SomeAskerError
is thrown if the inpout can't be
parsing into a value of the correct type, if the input fails the Asker'
s
predicate, or if the escape key is pressed.
ask' :: (MonadIO m, MonadCatch m) => Asker m a b -> m b Source
See ask
. Always reads the input from stdin.
ask' a = ask a Nothing
askEither :: (MonadIO m, MonadCatch m) => Asker m a b -> Maybe Text -> m (Either SomeAskerError b) Source
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.
Pressing the escape key returns a AskerInputAborterError
(if supported).
untilValid :: forall m a. (MonadIO m, MonadCatch m, Read a) => m a -> m a Source
Repeatedly executes an ask action until the user enters a valid value. Error messages are printed each time.
Creating predicates
boolPredicate :: Functor m => (a -> m Bool) -> (a -> PredicateError) -> Predicate' m a Source
Creates a predicate from a boolean function and an error message.
Example askers
A few askers for convenience.
data PathRootDoesNotExist Source
Indicates that no part of a path exists.
data PathIsNotWritable Source
Indicatres that the last existing portion of a path is not writable.
filepathAsker :: MonadIO m => PromptMsg -> (FilePath -> TypeError) -> Predicate m (PathExistenceType, FilePath) b -> Asker m FilePath b Source
Asks the user for a file or a directory.
Parsing checks for basic validity via isValid
. Invalid paths are rejected.
After that, the asker determines whether the target exists and what type it has. You can run a predicate on that information.
writablefilepathAsker :: MonadIO m => PromptMsg -> (FilePath -> TypeError) -> Predicate m (PathExistenceType, FilePath) b -> Asker m FilePath b Source
See filepathAsker
. This Asker
also ensures that the given path
is writeable in the following sense:
- at least some initial part of the path exists and
- the last existing part of the path is writeable.
PathRootDoesNotExist
and PathIsNotWritable
exceptions are thrown if the
first or second of these conditions is violated.
For relative paths, we only check that the current directory is writable.
Handled exceptions: