(Reply-To: haskell-cafe) Alastair Reid <alastair@reid-consulting-uk.ltd.uk> writes:
I have a question about error reporting. You use 'error' quite often. I think that this can cause errors to pop up at strange moments during program evaluation.
You're right, it can lead to late error messages. For example, if two output files are specified then the program might read its input, spend some time processing and only report an error after some considerable time has passed. (I haven't actually seen this happen but I'm sure it would.)
I'm not sure this brings anything new on the table, but after this thread I went back and reworked some of my option handling code. Hopefully it will either be an inspirational example, or provide me with feedback on my grave errors and mistakes :-) here it is: ------------------------------------------------------------
-- Args is a record for the arguments, easily accessible by field -- in the program proper
data Output = G | C | X deriving (Read,Eq) data Args = Args { kval :: Int, output :: Output, writer :: String -> IO (), parser :: Fasta.FHParser}
-- usage prints using "error", this is perhaps Not Nice? (it -- makes it rather difficult to print usage and exit with success, -- should that happen to be important)...
usage :: [String] -> a usage errs = error (usageInfo (concat errs ++ "\nUsage: xegen -k <kval> [-u] -{G|C|X} [-o FILE] <filename>\n") options)
-- ...but since usage has polymorphic type, I can use it to -- initialize required fields in the default argument struct:
defaultArgs = Args { kval = usage ["You must specify a k value"] , output = usage ["Please specify -K, C, or X"] , writer = putStr, parser = bmfparser }
-- mkK is used to parse the argument string for the -k option -- which needs to be an integer. This is IMHO superior to getting an -- anonymous "Prelude.read: no parse" message
-- add a k value to p mkK :: Args -> String -> Args mkK p s = p {kval = if (and $ map isDigit s) then read s else usage ["The k value must be an integer"]}
-- and here's the table of options
options :: [OptDescr (Args -> Args)] options = [ Option ['k'] ["word-size"] (ReqArg (\s p -> mkK p s) "INT") "Word size" ,Option ['G'] ["graph-output"] (NoArg (\p -> p { output = G })) "Output the graph in GraphViz format" ,Option ['C'] ["consensus-output"] (NoArg (\p -> p { output = C })) "Output assembled consensus sequences" ,Option ['X'] ["exons-output"] (NoArg (\p -> p { output = X })) "Output the (concatenated) exons" ,Option ['u'] ["input-upper"] (NoArg (\p -> p { parser = ugparser })) "accept only upper case characters" ,Option ['o'] ["output"] (ReqArg (\s p -> p { writer = writeFile s }) "FILE") "output file name (default is stdout)" ]
-- comments are most welcome!
(Another issue with error reporting is that I should probably print the 'usage' message whenever flags are incorrectly omitted, duplicated, etc.
I think that's achieved in the above. And I think the late error message problem can be solved by making sure the Args struct is entirely evaluated before any "real code" is run. -kzm -- If I haven't seen further, it is by standing in the footprints of giants