-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/
-- | High-level library for building command line interfaces.
--
-- This module provides a set of functions for building simple
-- command-line interfaces. It allows interfaces which collect values
-- (such as Integers, Dates, or other structured values), build lists of
-- values, and use simple menus. It is not intended to build complex
-- interfaces with full cursor control. It is oriented towards line-based
-- interfaces.
@package HCL
@version 1.9
-- | This module provides a set of functions for building simple
-- command-line interfaces. It allows interfaces which collect values
-- (such as Integers, Dates, or other structured values), build lists of
-- values, and use simple menus. It is not intended to build complex
-- interfaces with full cursor control. It is oriented towards line-based
-- interfaces.
--
-- Requests
--
-- The central concept of the library is the Request type, which
-- embodies an interactive request for data. When requesting data, there
-- is always the possibility of failure. That is, the user may enter a
-- value that doesn't parse, or may want to quit the process. For this
-- reason, the value stored by a request is IO (Maybe
-- a), which shows there may not always be a value available.
-- Request is a monad, and when a request fails, no subsequent
-- requests are asked. Instead, the whole request chain is abandoned.
--
-- The function reqResp gives the most basic request possible,
-- which is for a string. From this, other requests can be built. The
-- library provides several:
--
--
-- - reqInt - Requests Int values.
-- - reqInteger - Requests Integer values.
-- - reqChar - Requests a single character (without waiting for
-- the user to press enter)
-- - reqPassword - Like reqResp, but doesn't echo the
-- user's input to the console.
-- - reqRead - Requests Read-able values.
-- - reqList - Asks a request repeatedly and builds a list of
-- the responses, which are returned when the user enters a failure
-- value.
-- - reqMenu - Given a list of items, asks the user to pick one
-- of the items and returns it.
-- - reqFail - Always results in failure. Useful in menus for
-- creating a "quit" or "none" selection.
--
--
-- A number of request patterns are also exported by the module. These
-- embody different control schemes that are useful when building
-- command-line interfaces. These include:
--
--
-- - reqIf - Takes a request which evaluates to a boolean and
-- two requests representing "then" and "else" branches. The appropriate
-- subsequent request is used, based on the value of the conditional
-- request.
-- - reqAgree - Takes a request and determines if the user
-- answers yes or no. A default can also be provided.
-- - reqForever - Takes a request and asks it over and over,
-- until a failure value appears.
-- - reqIterate - Takes a function which, given a value,
-- produces a request. An initial Request value is also provided.
-- The initial value is given to the function, and the value produced by
-- the function is fed back into it. This continues until a failure
-- occurs. This model is useful for shell-type applications which take a
-- state, operate on it, and produce a new state, which is then fed back
-- in.
-- - reqCont - Takes a request and a "continuation" request. If
-- the first request fails, the "continuation" request is run. This is
-- useful for confirming if the user really wants to quit an application,
-- or other escape mechanisms.
--
--
-- Running Requests
--
-- Requests can be run with two different functions:
--
--
-- - execReq - Takes a request, runs it, and returns a
-- meaningless value. This is most often used to run a request from
-- 'main'.
-- - runRequest - Runs a request and returns the raw
-- IO ("Maybe a") value returned. This is useful for
-- running a request and extracting the value returned out of it.
--
--
-- Prompting
--
-- In most req functions, except reqMenu and reqChoices,
-- nothing is printed to the screen. Instead, a set of functions is
-- provided which take a request and a string to use as a prompt. These
-- functions include:
--
--
-- - prompt - Displays a message and gets a response. If the
-- message ends in a space, it is assumed that input should be typed on
-- the same line. Otherwise, a newline is printed and input is then
-- gathered.
-- - prompt1 -- Simple way to ask for a response and provide a
-- default.
-- - promptAgree -- Simple way to ask for a yes/no
-- response.
--
--
-- Simple Programs
--
-- Getting values combines prompting and requests. Here's a 'guess a
-- number' game which probably isn't real fun (from
-- examples\guess_num.hs):
--
--
-- guess_num_boring =
-- do
-- num <- prompt "Enter your guess between 1 - 100: " reqInt
-- if num == 50
-- then reqIO $ putStrLn "You win!"
-- else reqIO $ putStrLn "Too bad!"
--
--
-- To run the program, type play_game guess_num_boring at the
-- prompt. A better program might actually randomize the number, and tell
-- you if you are low or high (again from examples\guess_num.hs):
--
--
-- guess_num_fun =
-- do
-- target <- reqIO $ getStdRandom (randomR (1::Integer,100))
-- let guessed val =
-- case compare target val of
-- GT -> do { reqIO $ putStrLn "Too low!"; return False }
-- LT -> do { reqIO $ putStrLn "Too high!"; return False }
-- EQ -> do { reqIO $ putStrLn "You win!"; return True }
-- reqUntil guessed (prompt "Enter a number between 1 and 100: " reqInteger)
--
-- play_game game = execReq game
--
--
-- To run the program, type play_game guess_num_fun at the
-- prompt. Several features of this program are worth pointing out:
--
--
-- - reqIO - This function is used to lift IO operations into
-- the Request type.
-- - reqUntil - This function takes a condition and a request,
-- and runs the request until the condition is satisfied. The conditional
-- has the type (a -> Request b), which allows the
-- conditional to produce output, or base its decision on other requests.
-- Naturally, the second argument has the type (Request a),
-- which means the result of the request can be passed to the condition.
-- Other functions which wrap up input patterns are reqFoldl,
-- reqList, reqCont, and others.
--
--
-- Combining Requests
--
-- The functions in this library are designed to allow more complex
-- Request values to be built from them. For example, imagine you
-- are coding for a tax form submission and have a data type like this
-- (from examples\taxpayer.hs):
--
--
-- data Taxpayer = Taxpayer { name :: String, age :: Int, ssn :: String }
-- deriving (Read, Show)
--
--
-- Because Taxpayer derives Read, a simple way of
-- collecting a Taxpayer value from the user would be:
--
--
-- reqTaxpayer :: Request Taxpayer
-- reqTaxpayer = prompt "Please enter tax payer information: " (reqRead reqResp)
--
--
-- Of course, this isn't very friendly:
--
--
-- *Main> getTaxpayer reqTaxpayer
-- Please enter tax payer information: Taxpayer {name="John", age = 30, ssn = "" }
-- You entered: Taxpayer {name = "John", age = 30, ssn = ""}
--
--
-- Typing Taxpayer { name = "John" ... } each time is pretty
-- tedious. A better solution builds the value from simpler pieces:
--
--
-- reqTaxpayerEasy :: Request Taxpayer
-- reqTaxpayerEasy =
-- do
-- name <- prompt "Please enter the tax payer's name: " reqResp
-- age <- prompt "Please enter their age: " reqInt
-- ssn <- prompt "What is their SSN/ASN: " reqResp
-- return (Taxpayer name age ssn)
--
--
-- Now, when tax payer info must be entered a nice set of prompts is
-- displayed:
--
--
-- *Main> getTaxpayer reqTaxpayerEasy
-- Please enter the tax payer's name: Bob
-- Please enter their age: 50
-- Please enter their SSN/ASN: 111-11-1111
-- You entered: Taxpayer {name = "Bob", age = 50, ssn = "111-11-1111"}
--
--
-- Validation
--
-- HCL provides the reqWhile and reqUntil functions which
-- help ensure values entered are correct. For example, in the above, we
-- could validate SSN's fairly easily like so (again, from
-- example\tax_payer.hs):
--
--
-- reqSSN :: Request String -> Request String
-- reqSSN req =
-- do
-- -- very simple validation
-- let
-- matchSSN = matchRegex (mkRegex "^...-..-....$")
-- invalidSSN ssn = return $ isNothing (matchSSN ssn)
-- ssn <- reqWhile invalidSSN req
-- return ssn
--
--
-- In the above, reqWhile repeatedly uses invalidSSN to
-- determine if the value entered matches the (very simple) regular
-- expression provided. When it does, the SSN entered is returned. Until
-- then, the request is asked over and over. One subtlety to note is that
-- a request to get the actual value is passed in to the function as
-- req. This allows the function reqTaxpayerValidate to
-- pass it's own prompt and request into reqSSN:
--
--
-- reqTaxpayerValidate :: Request Taxpayer
-- reqTaxpayerValidate =
-- do
-- name <- prompt "Please enter the tax payer's name: " reqResp
-- age <- prompt "Please enter their age: " reqInt
-- ssn <- reqSSN (prompt "What is their SSN/ASN: " reqResp)
-- return (Taxpayer name age ssn)
--
--
-- Running reqTaxpayerValidate from the prompt then gives:
--
--
-- *Main> getTaxpayer reqTaxpayerValidate
-- Please enter the tax payer's name: Bob
-- Please enter their age: 20
-- What is their SSN/ASN: 324=12=1231
-- What is their SSN/ASN: 324-12-1211
-- You entered: Taxpayer {name = "Bob", age = 20, ssn = "324-12-1211"}
--
--
-- Dealing with Failure
--
-- A fundamental assumption of the Request type is that requests
-- can fail. The user can enter no input or provide bad input. The
-- discussion of validation above is a bit disingenuous because it does
-- not mention what happens when the user just types a newline at the
-- prompt. In all cases, the request chain ends and the program exits.
--
-- This is due to the behavior of the Request monad - as soon as
-- one request fails, the rest fail. The library provides several
-- functions for dealing with this:
--
--
-- - reqDefault - Allows a default value to be supplied, which
-- will be returned if the user provides no input or bad input.
-- - required - Repeatedly asks a request until the user
-- provides input. "Failure" values will not occur.
-- - reqCont - Takes two request arguments. If the first fails,
-- the second is used. Useful for providing a "continuation" to a request
-- chain.
-- - reqWhich - Indicates if a request failed or not, through
-- the use of the Either type. There is no direct way to determine
-- if a request failed (that is, if it evaluates to Nothing, the entire
-- chain fails and you won't see it). This function allows some
-- visibility into if a specific request succeeded or not.
--
--
-- One use for reqCont is to confirm if the user really wants to
-- quit a program. In the guess-a-number game, hitting Enter at a prompt
-- stops the game. This can be avoided by changing how the guess a number
-- game is launched:
--
--
-- guess_num_cont =
-- reqCont guess_num_fun confirm
-- where
-- confirm =
-- reqIf (promptAgree "Are you sure you want to quit? " (Just False) reqResp)
-- reqFail
-- guess_num_cont
--
--
-- Above, reqCont will run guess_num_fun until it returns
-- a Just value. If Nothing is returned, then
-- reqConfirm is run. If the user does not wish to quit,
-- reqConfirm will run guess_num_confirm again.
-- Otherwise, reqFail is run, which causes the request to fail and
-- thus the program to exit. Notice that the confirmation behavior was
-- added by just adding another layer to the request chain. The
-- guess_num_fun function was used to provide gameplay -
-- guess_num_confirm just added a layer to control when the game
-- ends.
--
-- However, because this pattern is fairly common, HCL provides the
-- reqConfirm function, which acts just like the reqCont
-- pattern above. That is, it takes a request to run and a request which
-- returns a Bool. If the initial request fails, the confirmation
-- request is run. If that request results in True, the failure
-- is allowed to propagate. Otherwise, the initial request is run again.
-- The function guess_num_confirm gives an example of its usage:
--
--
-- guess_num_confirm =
-- reqConfirm confirm guess_num_fun
-- where
-- confirm = promptAgree "Are you sure you want to quit? " (Just False) reqResp
--
--
-- Making Menus
--
-- Several functions are used to build simple, hierarchical menus. A menu
-- is defined as a list of pairs, where the first element is the label
-- and the second a value to return. Usually, that value is a
-- Request. In some cases it is not. There are two functions used
-- for building menus:
--
--
-- - reqChoices - A low-level means to build menus. It does not
-- expect the second item in the pair to be a request, and is thus very
-- general.
-- - reqMenu - Expects the list given to be a pair of a string
-- and another request. When an item is selected, that request is run and
-- the value is returned.
-- - reqSubMenu - Inserts a menu into a menu. When the item for
-- the submenu is selected, the submenu will display its choices. When
-- the user wishes to exit (by providing a failure value), the previously
-- displayed menu will display again.
-- - reqMenuItem - Constructs an indvidual menu item.
-- - reqMenuEnd - Indicates the end of a list of menu
-- items.
-- - reqMenuExit - A specialized menu item which will cause the
-- menu request to fail. That means we return to the previous menu or
-- exit the request chain altogether, depending on how the menus are
-- structured.
--
--
-- reqMenu and reqSubMenu work together to build
-- hierarchical menus in which the user can automatically navigate "up"
-- by just hitting return. For example, imagine a simple menu-driven PIM:
--
--
-- *Main> pim
-- 1. Manage contacts
-- 2. Manage calendar
-- ? 1
-- 1. Add a contact
-- 2. Remove a contact
-- ? <-- User hits return here, returns to main menu
-- 1. Manage contacts
-- 2. Manage calendar
-- ?
--
--
-- Setting this up is fairly straightforward (from examples\pim.hs):
--
--
-- pim = execReq $ reqConfirm confirm topMenu
-- where
-- confirm = promptAgree "Are you sure you want to quit?" (Just False) reqResp
--
-- topMenu =
-- reqMenu $
-- -- Insert a submenu defined elsewhere
-- reqSubMenu topMenu "Manage contacts" manageContactsMenu $
-- -- Insert a sub menu directly
-- reqSubMenu topMenu "Manage calendar"
-- (reqMenuItem "Add an event" notImpl $
-- ...
-- reqMenuExit "Return to previous menu"
-- reqMenuEnd) $
-- ...
-- -- End the menu definition
-- reqMenuEnd
--
-- -- Defines a partial menu
-- manageContactsMenu =
-- reqMenuItem "Add a contact" notImpl $
-- ...
-- reqMenuExit "Return to previous menu"
-- reqMenuEnd
--
-- notImpl = reqIO $ putStrLn "This function is not implemented."
--
--
-- reqMenu begins the process of definining a menu.
-- reqMenuItem is used to build a menu item, and when combined
-- with ($) as above can be used to define a list of menu items
-- "in-line". reqSubMenu takes the menu to return to as its first
-- argument (in the case above, topMenu), a label to name the
-- menu item, and a request which will become the submenu. As seen above,
-- submenus can be inserted directly (e.g. "Manage calendar"), or they
-- can be defined independently (e.g. "Manage contacts").
-- reqMenuExit allows the submenu to return to control to its
-- calling menu. Finally, reqMenuEnd can be used to end an
-- "in-line" menu definition.
--
-- Just Plain Cool
--
-- Some of the other functions included are just cool to use:
--
--
-- - reqIterate - This take a function which maps a value to a
-- request and a request. The request is evaluated and the results passed
-- to the function. The result of that function is passed back into the
-- function again. reqIterate is useful for applications that
-- manipulate some sort of environment by repeatedly passing the modified
-- environment back into themselves. An example of this is shown in
-- examples\shell.hs where the shell function is repeatedly
-- called from main using reqIterate. The hangman game in
-- hangman\hangman.hs also uses this when the playRound function
-- is repeatedly called from main.
-- - reqFoldl - Like foldl, but for requests. The
-- accumulating function takes values of type a (which come from the
-- request given) and type b (the accumulating value) and produces a
-- Request of type b. If and when the initial request fails,
-- whatever accumulated value that was built is returned.
-- - reqList - Takes a request and repeatedly runs it, building
-- a list of the results. When the request fails, the list is
-- returned.
-- - makeReq - Not really so cool, but allows you to construct
-- your own Request values. Values created with makeReq can
-- be extracted with runRequest. However, they will come back with
-- the type (IO ("Maybe a"), where the value is always a
-- Just value.
--
--
-- Examples
--
-- Several examples are included with the library, including a hangman
-- game you can play:
--
--
-- - examples\guess_num.hs - Demonstrates various ways of implementing
-- a "guess a number" game.
-- - examples\pim.hs - Shows how to build simple menus.
-- - examples\shell.hs - Shows how to use reqIterate to build a simple
-- shell.
-- - examples\tax_payer.hs - Demonstrates how to construct requests for
-- specific structured data types from simpler requests.
-- - hangman\hangman.hs - Implements the hangman game. An executable is
-- installed when you install the library - just run hangman at
-- the command line.
--
module System.Console.HCL
-- | The Request data type represents a value requested
-- interactively. The request may have failed or been no response, in
-- which case the request fails. Otherwise, the request holds the
-- response given.
newtype Request a
Request :: IO (Maybe a) -> Request a
-- | Extracts the value from a given request.
runRequest :: Request a -> IO (Maybe a)
-- | Runs a request, throws away the result, and returns an IO
-- type (rather than a Request). Useful when a request should
-- just be run and we don't care about the result. Generally used at the
-- top level to evaluate a request in main.
execReq :: Request a -> IO ()
-- | Allows IO operations in the Request type. If
-- the IO operation throws an IOError, the
-- resulting Request will return Nothing. Same
-- as liftIO in MonadIO class (in
-- Control.Monad.Trans@ module)
reqIO :: IO a -> Request a
-- | Lifts a Maybe a into a Request a.
reqLiftMaybe :: Maybe a -> Request a
-- | Takes a value and makes it into a request. Should not be an
-- IO (Maybe a) type value, unless multiply nested
-- values is desired.
makeReq :: a -> Request a
-- | The basic request - get a string from the user. If a newline or all
-- whitespace is entered, the request is assumed to be a failure.
reqResp :: Request String
-- | Gets an Integer from the user. If the value entered
-- cannot be converted, the request fails.
reqInteger :: Request Integer
-- | Gets an Int from the user. If the value entered cannot
-- be converted, the request fails.
reqInt :: Request Int
-- | Uses "reads" to process a request. If the value cannot be
-- parsed, fails. Otherwise, returns the value parsed.
reqRead :: Read a => Request String -> Request a
-- | reqChar requests a single character. Unlike other
-- Requests, it does not wait for the user to hit enter;
-- it simply returns the first keystroke.
reqChar :: Request Char
-- | reqPassword works like reqResp except
-- that it does not echo the user's input to standard output.
reqPassword :: Request String
-- | && operator for requests (with failure). Behaves
-- similarly, including "short-circuit" behavior. If either condition
-- fails, the entire Request fails.
andReq :: Request Bool -> Request Bool -> Request Bool
-- | || operator for requests (with failure). Behaves similarly,
-- including "short-circuit" behavior. If either condition fails, the
-- entire Request fails.
orReq :: Request Bool -> Request Bool -> Request Bool
-- | not operator for requests.
notReq :: Request Bool -> Request Bool
-- | If statement for requests.
reqIf :: Request Bool -> Request a -> Request a -> Request a
-- | Takes a value and makes it into a request.
reqConst :: a -> Request a
-- | Lifts a one-argument function into Request types.
reqLift :: (a -> b) -> Request a -> Request b
-- | Lifts a two argument function into Request types. The
-- arguments to the function are evaluated in order, from left to right,
-- since the Request Monad imposes
-- sequencing.
reqLift2 :: (a -> b -> c) -> Request a -> Request b -> Request c
-- | Like the "maybe" function, but for requests. Given a request
-- value, a default value, and a function that maps a to
-- Request b, this function either returns the default if
-- the request value is Nothing or an IOError is
-- thrown, or it applies the function given to the value of the request
-- and returns it.
reqMaybe :: Request a -> Request b -> (a -> Request b) -> Request b
-- | Returns true if the user answer y or Y. Allows a
-- default to be specified, and allows failure if no default is given.
reqAgree :: Maybe Bool -> Request String -> Request Bool
-- | Automatic failure. Useful in menus to quit or return to the previous
-- menu.
reqFail :: Request a
-- | Takes a request and guarantees a value will be returned. That is, the
-- request is repeated until a valid (i.e. not Nothing) response
-- is returned.
required :: Request a -> Request a
-- | Runs the request until the condition given is satisfied, then returns
-- the first result that satisfies it. If either request or condition
-- return Notthing the result will also be Nothing.
reqUntil :: (a -> Request Bool) -> Request a -> Request a
-- | Runs the request while the condition given holds, then returns the
-- first result where it doesn't. Good for verification. If either
-- request or condition return Nothing at any point, the reault
-- will also be Nothing.
reqWhile :: (a -> Request Bool) -> Request a -> Request a
-- | Requests a response from user. If Nothing is returned or an
-- IOError is thrown, assumes default and returns that.
reqDefault :: Request a -> a -> Request a
-- | Ask a request forever -- until failure.
reqForever :: Request a -> Request a
-- | Given a list of items and programs to run, displays a menu of the
-- items and runs the selected program. Very low level - usually
-- reqMenu is used instead. If the user selects an invalid
-- choice, failure occurs.
reqChoices :: [(String, a)] -> Request Int -> Request a
-- | Takes an initial value and function which produces a request from that
-- value. Applies the function to the initial value and then recurses.
-- Useful for functions which operate off their own output (e.g. a shell
-- maintaining an environment).
reqIterate :: (a -> Request a) -> a -> Request a
-- | Takes a request and a "continuation" request. If the first request
-- results in Nothing or an IOError is thrown,
-- run the second request. In either case, return the result of the
-- successful request.
reqCont :: Request a -> Request a -> Request a
-- | Executes the request given and, if a failure value occurs, executes
-- the Bool request given (usually some sort of prompt
-- asking if they want to quit). If the answer is True, the
-- failure value propagates. Otherwise, the initial request is run again.
reqConfirm :: Request Bool -> Request a -> Request a
-- | Indicates if the request failed or succceeded. If Left
-- () is returned, the request failed. If Right v is
-- returned, the request produced a value. Though the value returned is
-- itself a request, it will always be valid. An IOError
-- being thrown by the original request is considered a failire.
reqWhich :: Request a -> Request (Either () a)
-- | Give a function from a -> b, an initial value, and a
-- Request for a, builds a
-- Request for b. When (Request
-- a) fails, then the function returns whatever (Request
-- b) has been built.
reqFoldl :: (a -> b -> Request b) -> b -> Request a -> Request b
-- | Given a request, builds a list of response. When the user enters
-- Nothing, the list building ends
reqList :: Request a -> Request [a]
-- | Takes a list of strings and requests and forms a menu out of them.
-- Menus can built using reqMenuItem, reqSubMenu,
-- reqMenuExit, and reqMenuEnd.
reqMenu :: [(String, Request a)] -> Request a
-- | Used to add an individual entry to a menu that is being built.
reqMenuItem :: String -> Request a -> [(String, Request a)] -> [(String, Request a)]
-- | Ends a list of menu item definitions.
reqMenuEnd :: [(String, Request a)]
-- | Creates a submenu within a menu. When the submenu exits, control
-- returns to the item specified.
reqSubMenu :: Request a -> String -> [(String, Request a)] -> [(String, Request a)] -> [(String, Request a)]
-- | Causes the program to exit from the current menu.
reqMenuExit :: String -> [(String, Request a)] -> [(String, Request a)]
-- | Prints a message and makes a request. If the message ends in a space,
-- it is assumed that the user should enter values on the same line.
-- Otherwise, a new line is printed and the reqeust is evaulated.
prompt :: String -> Request a -> Request a
-- | Deprecated name for prompt1.
promptWithDefault :: Show a => String -> Request a -> a -> Request a
-- | Displays a message prompt and a default choice in a common way. If the
-- user doesn't provide a choice or enters bad data, the default value
-- provided is returned. Otherwise, the value entered is returned.
prompt1 :: Show a => String -> Request a -> a -> Request a
-- | Prints a message, displays defaults (if any), and turns a Request
-- String into a Request Bool. If a default value is
-- provided, it will be returned if the user enters nothing or an invalid
-- response.
promptAgree :: String -> Maybe Bool -> Request String -> Request Bool
instance Test.QuickCheck.Arbitrary.Arbitrary a => Test.QuickCheck.Arbitrary.Arbitrary (System.Console.HCL.RandomRequest a)
instance GHC.Show.Show a => GHC.Show.Show (System.Console.HCL.RandomRequest a)
instance GHC.Base.Functor System.Console.HCL.Request
instance GHC.Base.Applicative System.Console.HCL.Request
instance GHC.Base.Monad System.Console.HCL.Request
instance Control.Monad.Fail.MonadFail System.Console.HCL.Request
instance GHC.Base.Alternative System.Console.HCL.Request
instance GHC.Base.MonadPlus System.Console.HCL.Request
instance Control.Monad.IO.Class.MonadIO System.Console.HCL.Request
instance Control.Monad.Catch.MonadThrow System.Console.HCL.Request
instance Control.Monad.Catch.MonadCatch System.Console.HCL.Request
instance Test.QuickCheck.Arbitrary.Arbitrary a => Test.QuickCheck.Arbitrary.Arbitrary (System.Console.HCL.Request a)
instance GHC.Show.Show a => GHC.Show.Show (System.Console.HCL.Request a)