Haskell command line options
I've written a basic head emulator that reads from stdIn:
import IO
import System
io lineList = interact (unlines . lineList . lines)
--Found this. Basically it takes all input from stdIn and reads it lazily.
main = do
[numLines] <- getArgs
io (take (read numLines))
--reads by lines.
And then I attempted to add command lines like so (source: http://leiffrenzel.de/papers/commandline-options-in-haskell.html)
import IO
import System
import System.Console.GetOpt
io lineList = interact (unlines . lineList . lines)
--Found this. Basically it takes all input from stdIn and reads it lazily.
main = do
args <- getArgs
let ( actions, nonOpts, msgs ) = getOpt RequireOrder options args
opts <- foldl (>>=) (return defaultOptions) actions
let Options { optNum = input,
optOut = output} = opts
input >>= output
data Options = Options {
optNum :: IO String,
optOut :: String -> IO ()
}
defaultOptions :: Options
defaultOptions = Options {
optNum = "10",
optOut = io (take optNum)
}
options :: [OptDescr (Options -> IO Options)]
options = [
Option ['n'] ["numlines"] (OptArg readNumLines ) "read x amount of lines"
]
readNumLines arg opt = return opt (go arg)
go w = io (take (read w))
--reads by lines.
Now, my experience here is running out. It seems like the only place I'm actually calling readNumL开发者_如何学JAVAines is from the Options, if the N switch is set. Now, if the N switch is not set, I want to run the readNumLines command with an arg of 10. I'm obviously not doing this right.
Thanks in advance :)
EDIT: So, after a bit of trolling my code, I've got it to a state where it doesn't throw any errors at me, but still won't compile: Not in scope: data constructor 'options', but I've defined options already? EDIT: I had changed the Options constructor, but now it's telling me that it can't match the expected type of IO Options with the actual type Options at the opts <- foldl (>>=) (return defaultOptions) actions
I tried removing actions but that didn't work.
EDIT: So I removed most of that problematic line, and it worked.
opts <- return defaultOptions
However, passing -n doesn't do anything, it only returns the top 10 lines.
Let's go over what the body of main
should do.
Read command-line arguments:
args <- getArgs
Parse the arguments:let ( actions, nonOpts, msgs ) = getOpt RequireOrder options args
Combine the arguments into a set of program parameters:opts <- foldl (>>=) (return defaultOptions) actions
Extract parameters:let Options { optNum = input, optOut = output} = opts
Use the parameters:input >>= output
The GetOpt library only deals with parsing command-line arguments. In your case, the only thing command line arguments can control is the number of lines of output, so the Options data structure should only contain the number of lines of output. There's no point in encoding it as a string, you can just use an int.
data Options = Options { optNum :: Int }
When you specify a command-line option, an ArgDescr is used to specify the parser for the option's argument. The parser takes a string and turns it into a program-specific data structure. What you want is to turn a string into a function that updates an Options value. Note that I took out the IO type (you don't need IO), which will require some minor changes elsewhere in your code.
readNumLines :: String -> Options -> Options
readNumLines n options = options {optNum = read n}
After parsing options, the default options are updated as directed by each command-line argument (remember, parsing produces functions that update an Options value), to generate the final options. You're using IO to do this step, but you really don't need to.opts <- foldl (>>=) (return defaultOptions) actions
Since the fields of Options
have changed, the option-extracting code in main has to be updated to let Options { optNum = opt_num } = opts
At this point, you have a variable that holds the actual number of lines of code that should be printed, based on the command line options, and you can do whatever you want with it.
精彩评论