
Hi, I'm a slight Haskell newbie, but I'm trying to write a terminal-like application that accepts simple commands with optional arguments, and can then execute them. Most of these commands will need IO, as later I will want to communicate over USB for most of them. I was hoping, though, that I could get some comments on the initial architecture I've been playing with [1]. I suspect I should be using some sort of monad to represent the commands, but I don't fully understand monads, and am not sure how it would apply in this context. Should I be using a monad here, and if so, how? Thanks in advance, Jeff Wheeler [1] http://media.nokrev.com/junk/cli/

Hi Jeff,
On Fri, Oct 17, 2008 at 01:29, Jeff Wheeler
I'm a slight Haskell newbie, but I'm trying to write a terminal-like application that accepts simple commands with optional arguments, and can then execute them. Most of these commands will need IO, as later I will want to communicate over USB for most of them.
Do you mean a terminal application that runs a "session", accepting commands interactively? If so, check out Shellac [1]. [1] http://www.cs.princeton.edu/~rdockins/shellac/home/ cheers, Arnar

Jeff Wheeler
I suspect I should be using some sort of monad to represent the commands, but I don't fully understand monads, and am not sure how it would apply in this context.
Should I be using a monad here, and if so, how?
At risk of stating the obvious, you will need to us the IO monad... When you say "use a monad to represent" the commands, that is perhaps not necessary. You can represent the commands as data, for example: data MyLanguage = Go | Stop | Left | Right runMyLanguage :: MyLanguage -> IO () The `runMyLanguage` function serves to transform commands as pure data into real IO actions. -- _jsn

Hi Jeff It sounds like maybe you just want an application that works a bit like 'cabal'. So with cabal the first argument is taken as the 'command' and then the rest are based on that: cabal build --some other --options --which may --or --may --not have --arguments Yi has a simple template for a script which should get you started, please find it attached. So here instead of processOptions, you might want, processCommand processCommand :: [ String ] -> IO () processCommand ("build" : args) = processBuildCommand args processCommand ("play" : args) = processPlayCommand args processCommand [] = putStrLn "You must supply a command" processCommand _ = putStrLn "Sorry I don't understand your command" --Probably out put help here as well processBuildCommand :: [ String ] -> IO () processBuildCommand = similar to the processOptions except now you are sure you are in a 'build' command you *might* even have a separate set of option descreptions for each command. hth allan Jeff Wheeler wrote:
Hi,
I'm a slight Haskell newbie, but I'm trying to write a terminal-like application that accepts simple commands with optional arguments, and can then execute them. Most of these commands will need IO, as later I will want to communicate over USB for most of them.
I was hoping, though, that I could get some comments on the initial architecture I've been playing with [1].
I suspect I should be using some sort of monad to represent the commands, but I don't fully understand monads, and am not sure how it would apply in this context.
Should I be using a monad here, and if so, how?
Thanks in advance, Jeff Wheeler
[1] http://media.nokrev.com/junk/cli/ _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- The University of Edinburgh is a charitable body, registered in Scotland, with registration number SC005336.

2008/10/17 allan
Hi Jeff
It sounds like maybe you just want an application that works a bit like 'cabal'. So with cabal the first argument is taken as the 'command' and then the rest are based on that:
cabal build --some other --options --which may --or --may --not have --arguments
Yi has a simple template for a script which should get you started, please find it attached.
So here instead of processOptions, you might want, processCommand
processCommand :: [ String ] -> IO () processCommand ("build" : args) = processBuildCommand args processCommand ("play" : args) = processPlayCommand args processCommand [] = putStrLn "You must supply a command" processCommand _ = putStrLn "Sorry I don't understand your command" --Probably out put help here as well
processBuildCommand :: [ String ] -> IO () processBuildCommand = similar to the processOptions except now you are sure you are in a 'build' command
you *might* even have a separate set of option descreptions for each command.
I wanted to throw in another idea, something I didn't come up with myself but used in omnicodec[1]. Now I don't remember where I picked up the idea: 1. Keep all option values as members of a type T 2. Define an instance of T with default values, dT 3. When using getOpt let the type of the options be something like [OptDescr (T -> IO T)] 4. To get the final set of values fold (>>=) over all the list of arguments returned from getOpt and using dT as the start value. Something like 'effectiveT <- foldl (>>=) dT arguments' In a tool I recently started working on I decided not to work in IO and instead I ended up with something like 'effectiveT = (foldl (.) id arguments) dT'. /M [1]: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/omnicodec -- Magnus Therning (OpenPGP: 0xAB4DFBA4) magnus@therning.org Jabber: magnus@therning.org http://therning.org/magnus identi.ca|twitter: magthe

2008/10/17 Magnus Therning
I wanted to throw in another idea, something I didn't come up with myself but used in omnicodec[1]. Now I don't remember where I picked up the idea:
This method is described in http://www.haskell.org/haskellwiki/High-level_option_handling_with_GetOpt. I have used it in the past and eventually end up with huge Config data structures and not much separation of unrelated parts of the computation. Of course, this is probably just me ;-) Alternatively (and this is where I have had more success) you can take several passes over the command arguments, using different parsers. Each parser just ignores what it doesn't recognise: parseArgs names = do args <- getArgs let info = parseWith infoOptions [] let options = parseWith argOptions defaultOptArgs let query = parseWith queryOptions defaultQuery return (info, options, query) where parseWith os z = case getOpt Permute os args of (o,_,_) -> foldr id z o Note that this method tends to ignore user error at the command line, but I'm sure a hybrid could be constructed that was more chatty but still quite clean at the back end. See http://www.dougalstanton.net/code/buses/ for this code in its wider context. Cheers, D -- Dougal Stanton dougal@dougalstanton.net // http://www.dougalstanton.net
participants (6)
-
allan
-
Arnar Birgisson
-
Dougal Stanton
-
Jason Dusek
-
Jeff Wheeler
-
Magnus Therning