[Haskell-cafe] ANNOUNCE: HCL v1.0 -High-level library for building
command line interfaces
Justin Bailey
jgbailey at gmail.com
Mon Jul 9 18:28:57 EDT 2007
I'm please to announce HCL 1.0 - a library for building command line
interfaces. The library exports a mix of low and high-level functions
for building programs which gather simple values, ask yes/no
questions, or present hierarchical menus. The library is not intended
to do complex, full-screen UIs ala ncurses - it is intended for
line-oriented interfaces.
Included with the library is a hangman game, so if nothing else you
can enjoy that.
Where do I get it?
=============
Download from Hackage at:
http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HCL-1.0
How do I use it it?
=============
It's Cabal-ized so, after downloading and unpacking:
runghc Setup.hs configure
runghc Setup.hs build
runghc Setup.hs install
Note I had some inconsistent results building with HEAD cabal, but the
version distributed with GHC works great (1.1.6.2).
To play hangman, execute 'hangman'. Once installed, you can look at it
in GHCi by loading the "HCL" module.
Tell me more!
==========
This library lets you do a lot of cool things. For example, here's a
simple guess a number game:
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)
"reqUntil" takes a predicate and asks for a response until the
predicate is true. In the case above, the user is asked to enter an
integer. Once they guess the number, the program ends.
The library makes it easy to gather structured values. For example,
imagine this data structure:
data Taxpayer = Taxpayer { name :: String, age :: Int, ssn :: String }
deriving (Read, Show)
One way to use HCL to get Taxpayer values from the user is to take
advantage of its Read instance:
reqTaxpayer :: Request Taxpayer
reqTaxpayer = prompt "Please enter tax payer information: " (reqRead reqResp)
But that is ugly because the user has to type a "Read"-able string:
> getTaxpayer reqTaxpayer
Please enter tax payer information: Taxpayer {name="John", age = 30, ssn = "" }
However, we can build a form of sorts and make life much easier for the user:
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)
Which looks like this:
> getTaxpayer reqTaxpayerEasy
Please enter the tax payer's name: Bob
Please enter their age: 50
Please enter their SSN/ASN: 111-11-1111
The library also makes simple hierarchical menus easy to build. The
below defines the menu structure for a hypothetical PIM:
reqMenu $
reqSubMenu topMenu "Manage contacts" manageContactsMenu $
reqSubMenu topMenu "Manage calendar"
(reqMenuItem "Add an event" ... $
...
reqMenuExit "Return to previous menu"
reqMenuEnd) $
-- End the menu definition
reqMenuEnd
-- Defines a partial menu
manageContactsMenu =
reqMenuItem "Add a contact" ... $
...
reqMenuExit "Return to previous menu"
reqMenuEnd
These and more examples are distributed with the library in the
examples directory.
Anything else?
===========
Feedback, praise and criticism are welcome. Feedback regarding
'idiomatic' usage is especially desired. This is a first for me so any
responses are appreciated.
The library was developed on Windows using GHC - please let me know of
any odd *nix and Hugs bugs.
This library was inspired in part by the Ruby HighLine library
(http://highline.rubyforge.org/). Many thanks Mark Jones for his
input on HCL's design and implementation.
More information about the Haskell-Cafe
mailing list