-- 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.6 -- | 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: -- -- -- -- 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: -- -- -- -- Running Requests -- -- Requests can be run with two different functions: -- -- -- -- 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: -- -- -- -- 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: -- -- -- -- 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: -- -- -- -- 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: -- -- -- -- 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: -- -- -- -- Examples -- -- Several examples are included with the library, including a hangman -- game you can play: -- -- 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. data 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. Same as -- liftIO in MonadIO class (in -- Control.Monad.Trans module) reqIO :: IO 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 b to -- Request a, this function either returns the default if the -- request value is nothing, 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 result. reqUntil :: (a -> Request Bool) -> Request a -> Request a -- | Runs the request while the condition given holds, then returns the -- result. Good for verification. reqWhile :: (a -> Request Bool) -> Request a -> Request a -- | Requests a response from user. If Nothing is returned, -- 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, 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. 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 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 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)