repl-toolkit-0.2.0.0: Toolkit for quickly whipping up command-line interfaces.

Safe HaskellNone
LanguageHaskell2010

System.REPL

Contents

Description

Functions to expedite the building of REPLs.

Synopsis

String-generic versions of Prelude Functions

putErr :: ListLikeIO full item => full -> IO () Source

Prints a string to stderr.

putErrLn :: ListLikeIO full item => full -> IO () Source

Prints a string, followed by a newline character, to stderr.

prompt :: (MonadIO m, ListLikeIO full item) => m full Source

Prints > and asks the user to input a line.

Prompts

prompt' :: (MonadIO m, ListLikeIO full item, ListLikeIO full' item') => full -> m full' Source

Prints its first argument and, in the same line, asks the user to input a line.

promptAbort :: (MonadIO m, ListLikeIO full item, ListLikeIO full' Char) => Char -> full -> m (Maybe full') Source

The same as prompt, but aborts as soon as the user presses a given key (commonly '\ESC'). This function temporarily tries to set the buffering mode to NoBuffering via hSetBuffering, which may not be supported. See the documentation of hSetBuffering for details.

Feture-rich reading of user-input

These functions automate parsing and validating command-line input via the Asker type.

type PromptMsg = Text Source

A prompt.

type TypeErrorMsg = Text Source

An error message indicating that a value wasn't able to be parsed.

type PredicateErrorMsg = Text Source

An error message indicating that a value failied a predicate.

type Predicate m a = a -> m Bool Source

A predicate which a value has to fulfil.

type Parser a = Text -> Either Text a Source

A parser which either returns a parsed value or an error message.

data Asker m a Source

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 predicate, being monadic, can perform arbitrarily complex tests, such as checking whether a given date is in the future, whether an item is in a database, whether a file with a given name exists, etc.

Constructors

Asker 

Fields

askerPrompt :: Text

The prompt to be displayed to the user.

askerParser :: Parser a

The parser for the input value.

askerPredicate :: a -> m (Either Text ())

The predicate which the input, once read, must fulfill. The Left side is an error message.

data AskFailure Source

Represents a failure during the running of an asking function. Either the input was incorrect in some way, or the process was aborted by the user.

Constructors

TypeFailure TypeErrorMsg

The input wasn't able to be parsed.

PredicateFailure PredicateErrorMsg

The parsed value failed a predicate.

ParamFailure Text

An incorrect number of parameters was passed.

NothingFoundFailure

No action was appropriate for the given input.

AbortFailure

The input was aborted by the user.

askerP :: (Monad m, Functor m) => PromptMsg -> PredicateErrorMsg -> Parser a -> Predicate m a -> Asker m a Source

Creates a general Asker with a custom parsing function and a predicate that the parsed value has to pass. If either the parsing or the predicate fail, one of the given error messages is displayed.

typeAskerP :: (Monad m, Functor m) => PromptMsg -> Parser a -> Asker m a Source

Creates an Asker which only cares about the type of the input.

maybeAskerP :: (Monad m, Functor m) => PromptMsg -> PredicateErrorMsg -> Parser a -> Predicate m a -> Asker m (Maybe a) Source

An asker which asks for an optional value. If only whitespace is entered (according to isSpace), it returns Nothing without further parsing or checking; otherwise, it behaves identically to asker.

Asking based on 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.

readParser :: Read a => TypeErrorMsg -> Text -> Either Text a Source

A parser based on readMaybe. This suffices for the parsing of most data types.

asker :: (Monad m, Functor m, Read a) => PromptMsg -> TypeErrorMsg -> PredicateErrorMsg -> Predicate m a -> Asker m a Source

Creates a general Asker with readMaybe as its parser. Using readMaybe is perfectly fine for most values, but it has two drawbacks:

  1. The user input is unpacked into a String and then parsed. This can incur a performance hit for large inputs.
  2. A Read-instance must be available for the expected type.

typeAsker :: (Monad m, Functor m, Read a) => PromptMsg -> TypeErrorMsg -> Asker m a Source

Creates an Asker based on Read which just cares about the type of the input.

predAsker Source

Arguments

:: (Monad m, Functor m) 
=> PromptMsg 
-> Text

Predicate error message.

-> (Text -> m Bool)

The predicate.

-> Asker m Verbatim 

Creates an Asker which takes its input verbatim as Text. The input thus only has to pass a predicate, not any parsing.

maybeAsker :: (Monad m, Functor m, Read a) => PromptMsg -> TypeErrorMsg -> PredicateErrorMsg -> Predicate m a -> Asker m (Maybe a) Source

An asker based on Read which asks for an optional value.

newtype Verbatim Source

A verbatim Text whose Read instance simply returns the read string, as-is. This is useful for askers which ask for strings without quotes.

Constructors

Verbatim 

Fields

fromVerbatim :: Text
 

Instances

Read Verbatim

Read-instance for Verbatim. Wraps the given value into quotes and reads it a a Text.

Running askers

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, MonadError SomeException m, Functor m) => Asker m a -> Maybe Text -> m a Source

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) => Asker m a -> m a Source

See ask. Always reads the input from stdin.

ask' a = ask a Nothing

askEither :: (MonadIO m, Functor m) => Asker m a -> Maybe Text -> m (Either AskFailure a) 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.

untilValid :: (MonadIO m, MonadError SomeException m, Functor 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.