Parsing command line arguments in Haskell using getOpt - getopt

I am trying to teach myself Haskell. As a sample program, I am writing a Spider solitaire player.
I am trying to write a command line parser using System.Console.GetOpt. I know there are easier ways to do argument parsing for this program, but I want to learn how to use the GetOpt module because I anticipate needing its sophistication later in other programs that I will be writing.
I am trying to add a "--help" option that just prints a usage message and then exits. I would also like to print usage messages if either of the arguments to the "--games" option or the "--suits" option are not valid integers (games >= 1 and <= 1000, suits == 1, 2, or 4). I will be passing the resulting Options data type to other parts of my program.
I am also getting an error that progName is not in scope. Isn't the case statement in parseArgs in the scope of the do block?
Here is my code, patched together from the examples in "Real World Haskell" and the Haskell wiki:
module Main (main) where
import System.Console.GetOpt
import System.Environment(getArgs, getProgName)
data Options = Options {
optGames :: Int
, optSuits :: Int
, optVerbose :: Bool
} deriving Show
defaultOptions = Options {
optGames = 1
, optSuits = 4
, optVerbose = False
}
options :: [OptDescr (Options -> Options)]
options =
[ Option ['g'] ["games"]
(ReqArg (\g opts -> opts { optGames = (read g) }) "GAMES")
"number of games"
, Option ['s'] ["suits"]
(ReqArg (\s opts -> opts { optSuits = (read s) }) "SUITS")
"number of suits"
, Option ['v'] ["verbose"]
(NoArg (\opts -> opts { optVerbose = True }))
"verbose output"
]
parseArgs :: IO Options
parseArgs = do
argv <- getArgs
progName <- getProgName
case getOpt RequireOrder options argv of
(opts, [], []) -> return (foldl (flip id) defaultOptions opts)
(_, _, errs) -> ioError (userError (concat errs ++ helpMessage))
where
header = "Usage: " ++ progName ++ " [OPTION...]"
helpMessage = usageInfo header options
main :: IO ()
main = do
options <- parseArgs
putStrLn $ show options

Here is the solution that I came up with:
module Main (main) where
import Control.Monad
import Control.Monad.Error
import System.Console.GetOpt
import System.Environment(getArgs, getProgName)
data Options = Options {
optGames :: Int
, optSuits :: Int
, optVerbose :: Bool
} deriving Show
defaultOptions = Options {
optGames = 1
, optSuits = 4
, optVerbose = False
}
options :: [OptDescr (Options -> Either String Options)]
options =
[ Option ['g'] ["games"]
(ReqArg (\g opts ->
case reads g of
[(games, "")] | games >= 1 && games <= 1000 -> Right opts { optGames = games }
_ -> Left "--games must be a number between 1 and 1000"
) "GAMES")
"number of games"
, Option ['s'] ["suits"]
(ReqArg (\s opts ->
case reads s of
[(suits, "")] | suits `elem` [1, 2, 4] -> Right opts { optSuits = suits }
_ -> Left "--suits must be 1, 2, or 4"
) "SUITS")
"number of suits"
, Option ['v'] ["verbose"]
(NoArg (\opts -> Right opts { optVerbose = True }))
"verbose output"
]
parseArgs :: IO Options
parseArgs = do
argv <- getArgs
progName <- getProgName
let header = "Usage: " ++ progName ++ " [OPTION...]"
let helpMessage = usageInfo header options
case getOpt RequireOrder options argv of
(opts, [], []) ->
case foldM (flip id) defaultOptions opts of
Right opts -> return opts
Left errorMessage -> ioError (userError (errorMessage ++ "\n" ++ helpMessage))
(_, _, errs) -> ioError (userError (concat errs ++ helpMessage))
main :: IO ()
main = do
options <- parseArgs
putStrLn $ show options
How can I improve this?

Related

Haskell The last statement in a 'do' block must be an expression error

Hello please can someone tell me where this error comes from ?
The error is
DKA-2-MKA.hs:39:5:
The last statement in a 'do' block must be an expression
argv <- getArgs
i checked intendation already and it didnt helped
import System.Environment
import System.Console.GetOpt
import Data.Maybe ( fromMaybe )
data Options = Options { optI :: Boo , optT :: Bool } deriving (Show)
defaultOptions :: Options
defaultOptions = Options { optI = False, optT = False }
options :: [OptDescr (Options -> Options)]
options = [ Option ['i'] ["I"]
(NoArg (\ opts -> opts {optI = True}))
"I is true"
, Option ['t'] ["T"]
(NoArg (\ opts -> opts {optT = True}))
"T is true"
]
programOpt :: [String] -> IO (Options, String)
programOpt argv =
case getOpt Permute options argv of
(o, n, [] ) -> return (foldl (flip id) defaultOptions o, n)
(o, _, [] ) -> ioError $ userError $ exactlyone ++ usageInfo header options
(_, _, errs) -> ioError $ userError $ concat errs ++ usageInfo header options
where
header = "Usage [OPTIONS...] filename"
exactlyone = "One input"
main :: IO ()
main = do
argv <-getArgs
DKA-2-MKA.hs:39:5: The last statement in a 'do' block must be an expression argv <- getArgs
39:5 indicates the error is at line 39, column 5. Which looks like argv <- getArgs
an "expression" is a bit of code that is syntactically valid, has a type, and can be evaluated. Examples of expressions include
let x = 1 in x + 2
if b then x else y
5
return ()
examples of not expressions include:
(1
let x = 5
if b then x
argv <-getArgs is not an expression, it's a dangling bit of syntax that doesn't make sense on its own; you can't ask ghci :t argv <-getArgs.
In order to understand why the last line in a do block must be an expression, search "desugaring do notation haskell" on this site or elsewhere (maybe someone else can recommend a specific resource). This will also be in any intro haskell book.

Couldn't match expected type ‘IO ()’ with actual type ‘(Controller -> IO ()) -> IO ()’

I'm a newbie in Haskell, I took this error when I try to configure my code. I understand that all of instructions in main() need to be an IO(), and the error happened because one of function I used (in Graphics.Gloss.Interface.IO.Animate) didn't return IO(). I wanna display the result of a genetic algorithm by using gloss package.
Here is my code:
module Main where
import Prelude as P
import Control.Monad.Random as Rand
import Data.Functor
import Data.IORef
import Graphics.Gloss.Interface.IO.Animate
import Graphics.Solution
import Graphics.Plot
import Args
import Task
import Parsing
import Genetic
import Control.Concurrent.Async
import Control.Concurrent.STM.TChan
import Control.Monad.STM
import Control.Arrow (first)
main :: IO ()
main = do
args <- parseOptions
opts <- loadEvolOptions (evolOptionsFileName args)
gen <- newStdGen
task#(Task _ twrs _ _) <- loadTask (inputFileName args) (fitnessFuncFileName args)
chan <- newTChanIO
asolution <- async $ solve chan gen opts task
dataRef <- newIORef []
finalSolutionRef <- newIORef Nothing
animateIO mode white $ const $ do
mfinsol <- readIORef finalSolutionRef
case mfinsol of
Just solution -> do
samples <- readIORef dataRef
return $ solutionPicture task solution (fitnessPlot samples)
Nothing -> do
msolution <- poll asolution
case msolution of
Nothing -> do
mv <- atomically $ tryReadTChan chan
case mv of
Nothing -> return ()
Just v -> modifyIORef dataRef (++[v])
samples <- readIORef dataRef
return $ fitnessPlot samples
Just esol -> case esol of
Left e -> fail $ show e
Right solution -> do
saveResult (outputFileName args) (filterTowers solution twrs)
writeIORef finalSolutionRef (Just solution)
samples <- readIORef dataRef
return $ solutionPicture task solution (fitnessPlot samples)
where mode = InWindow "test_genetic_al" (1280, 1024) (10, 10)
fitnessPlot ds = translate (-300) (-200) $ scale 600 600 $ plot "generation" "fitness" $ first fromIntegral <$> ds
And this is which I got:
Couldn't match expected type ‘IO ()’
with actual type ‘(Controller -> IO ()) -> IO ()’
In a stmt of a 'do' block:
animateIO mode white
$ const
$ do { mfinsol <- readIORef finalSolutionRef;
case mfinsol of {
Just solution -> do { ... }
Nothing -> do { ... } } }
In the expression:
do { args <- parseOptions;
opts <- loadEvolOptions (evolOptionsFileName args);
gen <- newStdGen;
task#(Task _ twrs _ _) <- loadTask
(inputFileName args) (fitnessFuncFileName args);
.... }
In an equation for ‘main’:
main
= do { args <- parseOptions;
opts <- loadEvolOptions (evolOptionsFileName args);
gen <- newStdGen;
.... }
where
mode = InWindow "test_genetic_al" (1280, 1024) (10, 10)
fitnessPlot ds
= translate (- 300) (- 200)
$ scale 600 600
$ plot "generation" "fitness" $ first fromIntegral <$> ds
I've been searching my problem over Google and stackoverflow for so many times but still cannot find a solution for this error. Please help me.
P/S: This is a guide line for Graphics.Gloss: https://hackage.haskell.org/package/gloss-1.11.1.1/docs/Graphics-Gloss-Interface-IO-Animate.html
Sorry again for my silly question, after I gave Lazersmoke's suggestion (which you can see below in the comment area), I got another error which very similar with the error I asking for:
I changed the line: animateIO mode white $ const $ do
Into: animateIO mode white (_ -> return ()) $ const $ do
Couldn't match type ‘Picture’ with ‘()’
Expected type: Controller -> IO ()
Actual type: Controller -> IO Picture
In the second argument of ‘($)’, namely
‘const
$ do { mfinsol <- readIORef finalSolutionRef;
case mfinsol of {
Just solution -> do { ... }
Nothing -> do { ... } } }’
In a stmt of a 'do' block:
animateIO mode white (\ _ -> return ())
$ const
$ do { mfinsol <- readIORef finalSolutionRef;
case mfinsol of {
Just solution -> do { ... }
Nothing -> do { ... } } }
In the expression:
do { args <- parseOptions;
opts <- loadEvolOptions (evolOptionsFileName args);
gen <- newStdGen;
task#(Task _ twrs _ _) <- loadTask
(inputFileName args) (fitnessFuncFileName args);
.... }
How many arguments does animateIO take?
animateIO :: Display
-> Color
-> (Float -> IO Picture)
-> (Controller -> IO ())
-> IO ()
Four. How many arguments did you provide to animateIO?
animateIO mode white $ …
Three. The type of
animateIO mode white $ …
is (Controller -> IO ()) -> IO (), which is exactly what your error message tells you. Since you don't want to use the Controller -> IO () part, you can provide your own animateIO:
animateIO' :: Display -> Color -> IO Picture -> IO ()
animateIO' m c a = animateIO m c (const a) (const $ return ())
Note that your (\_ -> return ()) did not work because the third argument has to produce a Picture, not a IO ().

How to convert from happs -> happstack?

Can anyone help me "translate" the below from happs to happstack:
module Main where
import HAppS.Server.AlternativeHTTP
import HAppS.Server.HTTP.AltFileServe
import Control.Monad.State
import Numeric
import Contracts
instance FromData ExContr where
fromData = do c <- look "contract"
arg1 <- look "arg1"
arg2 <- look "arg2"
img <- look "image"
return $ ExContr (c, map fst $ readFloat arg1
++ readFloat arg2, read img)
main :: IO ()
main = do simpleHTTP [dir "contractEx"
[withData $ \(ExContr t) ->
[anyRequest $ liftIO $ liftM toResponse =<< renderEx (ExContr t)]
,anyRequest $ ok $ toResponse renderExDefault]
,fileServe ["Contracts.html"] "public" -- fileserving
]
Contracts.hs contains:
newtype ExContr = ExContr (String, [Double], Bool) deriving (Read,Show,Eq)
renderEx :: ExContr -> IO Html
renderEx exSpec#(ExContr (contractId, args, lattice)) =
let pr = evalEx exSpec
expValChart = if contractId == "probs" then noHtml -- expected value is meaningless for the probabilities it relies on
else h3 << "Expected value" +++ image ! [src (chartUrl $ expectedValuePr pr)]
imageType = "png"
in if useLatticeImage exSpec
then do baseName <- mkUniqueName baseDotFilename
exitCode <- latticeImage pr (webPath ++ tmpImgPath ++ baseName) imageType
let pageContents =
case exitCode of
ExitSuccess -> renderExampleForm exSpec (image ! [src latticeUrl, border 1]) expValChart
where latticeUrl = "/" ++ tmpImgPath ++ baseName ++ "." ++ imageType
_ -> p << "renderEx: error generating lattice image"
return $ renderExamplePage pageContents
else return $ renderExamplePage $ renderExampleForm exSpec (prToTable pr) expValChart
renderExDefault = renderExamplePage $
renderExampleForm (ExContr ("zcb", [fromIntegral t1Horizon, 10], True))
noHtml noHtml
Alternatively I would like to understand how to install an old version of HappS compatible with the above code. Needless to say I am very new to Haskell.
This should work, assuming your ExContr type and renderEx functions that you did not supply in your code are similar to what I have here. I cannot actually run your code to ensure that it behaves the same.
module Main where
import Control.Monad
import Control.Monad.Trans (liftIO)
import Happstack.Server.Internal.Monads (anyRequest)
import Happstack.Server.SimpleHTTP
import Happstack.Server.FileServe
import Numeric
-- data ExContr = ExContr (String, [Double], String)
-- renderEx :: ExContr -> IO String
-- renderEx = undefined
instance FromData ExContr where
fromData = do c <- look "contract"
arg1 <- look "arg1"
arg2 <- look "arg2"
img <- look "image"
return $ ExContr (c, map fst $ readFloat arg1
++ readFloat arg2, read img)
main :: IO ()
main = do
simpleHTTP (nullConf { port = 80 }) $ msum [
dir "contractEx" $ withData $ \(ExContr t) -> msum $ [
anyRequest $ fmap toResponse $ liftIO $ renderEx (ExContr t)
, anyRequest $ ok $ toResponse renderExDefault
]
, serveDirectory DisableBrowsing ["Contracts.html"] "public"
]
Edited: forgot the renderExDefault line.

zip AST with bool list

I have an AST representing a haskell program and a bitvector/bool list representing the presence of strictness annotations on Patterns in order.For example, 1000 represents a program with 4 Pats where the first one is a BangPat. Is there any way that I can turn on and off the annotations in the AST according to the list?
-- EDIT: further clarify what I want editBang to do
Based on user5042's answer:
Simple.hs :=
main = do
case args of
[] -> error "blah"
[!x] -> putStrLn "one"
(!x : xs) -> putStrLn "many"
And I want editBang "Simple.hs" [True, True, True, True] to produce
main = do
case args of
[] -> error "blah"
[!x] -> putStrLn "one"
(!(!x : !xs)) -> putStrLn "many"
Given that above are the only 4 places that ! can appear
As a first step, here's how to use transformBi:
import Data.Data
import Control.Monad
import Data.Generics.Uniplate.Data
import Language.Haskell.Exts
import Text.Show.Pretty (ppShow)
changeNames x = transformBi change x
where change (Ident str) = Ident ("foo_" ++ str)
change x = x
test2 = do
content <- readFile "Simple.hs"
case parseModule content of
ParseFailed _ e -> error e
ParseOk a -> do
let a' = changeNames a
putStrLn $ ppShow a'
The changeNames function finds all occurrences of a Ident s and replaces it with Ident ("foo_"++s) in the source tree.
There is a monadic version called transformBiM which allows the replacement function to be monadic which would allow you to consume elements from your list of Bools as you found bang patterns.
Here is a complete working example:
import Control.Monad
import Data.Generics.Uniplate.Data
import Language.Haskell.Exts
import Text.Show.Pretty (ppShow)
import Control.Monad.State.Strict
parseHaskell path = do
content <- readFile path
let mode = ParseMode path Haskell2010 [EnableExtension BangPatterns] False False Nothing
case parseModuleWithMode mode content of
ParseFailed _ e -> error $ path ++ ": " ++ e
ParseOk a -> return a
changeBangs bools x = runState (transformBiM go x) bools
where go pp#(PBangPat p) = do
(b:bs) <- get
put bs
if b
then return p
else return pp
go x = return x
test = do
a <- parseHaskell "Simple.hs"
putStrLn $ unlines . map ("before: " ++) . lines $ ppShow a
let a' = changeBangs [True,False] a
putStrLn $ unlines . map ("after : " ++) . lines $ ppShow a'
You might also look into using rewriteBiM.
The file Simple.hs:
main = do
case args of
[] -> error "blah"
[!x] -> putStrLn "one"
(!x : xs) -> putStrLn "many"

Haskell Yahoo Finance Command Line Tool

I am trying to use this module to create a command line tool that will grab a quote from yahoo finance for a symbol. When I try to compile I receive this error.
Couldn't match expected type `[t0]'
with actual type `IO
(Maybe (Map (QuoteSymbol, QuoteField) QuoteValue))'
In the return type of a call of `getQuote'
In a stmt of a 'do' expression:
q <- getQuote [arg] ["s", "l1", "c"]
In the expression:
do { q <- getQuote [arg] ["s", "l1", ....];
case q of {
Nothing -> error "symbol not found"
Just m -> m } }
I am just starting to learn haskell, which is an incredibly different but powerful language, and I can not quite figure out what the problem is. Any help would be greatly appreciated and I would also appreciate any feedback on if I'm doing this the "haskell" way and if not any improvements I can make. Thanks!
module Main where
import Finance.Quote.Yahoo
import Data.Time.Calendar
import Data.Map
import System( getArgs )
import System.Console.GetOpt
import Data.Maybe ( fromMaybe )
data Options = Options
{ optVerbose :: Bool
, optShowVersion :: Bool
, optOutput :: Maybe FilePath
, optInput :: Maybe FilePath
, optLibDirs :: [FilePath]
, optSymbol :: String
} deriving Show
defaultOptions = Options
{ optVerbose = False
, optShowVersion = False
, optOutput = Nothing
, optInput = Nothing
, optLibDirs = []
, optSymbol = "YHOO"
}
options :: [OptDescr (Options -> Options)]
options =
[ Option ['v'] ["verbose"]
(NoArg (\ opts -> opts { optVerbose = True }))
"chatty output on stderr"
, Option ['V','?'] ["version"]
(NoArg (\ opts -> opts { optShowVersion = True }))
"show version number"
, Option ['o'] ["output"]
(OptArg ((\ f opts -> opts { optOutput = Just f }) . fromMaybe "output")
"FILE")
"output FILE"
, Option ['c'] []
(OptArg ((\ f opts -> opts { optInput = Just f }) . fromMaybe "input")
"FILE")
"input FILE"
, Option ['L'] ["libdir"]
(ReqArg (\ d opts -> opts { optLibDirs = optLibDirs opts ++ [d] }) "DIR")
"library directory"
, Option ['s'] ["symbol"]
(ReqArg (\ s opts -> opts { optSymbol = getSymbol s }) "SYMBOL")
"symbol SYMBOL"
]
compilerOpts :: [String] -> IO (Options, [String])
compilerOpts argv =
case getOpt Permute options argv of
(o,n,[] ) -> return (foldl (flip id) defaultOptions o, n)
(_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
where header = "Usage: ic [OPTION...] files..."
main = do
args <- getArgs
compilerOpts args
getSymbol :: String -> String
getSymbol arg = do
q <- getQuote [arg] ["s", "l1", "c"]
case q of
Nothing -> error "symbol not found"
Just m -> m
The trouble is that getSymbol has no business in the definition of options, since it is an IO action as we can see from the fact that it uses getQuote:
getQuote :: [QuoteSymbol]
-> [QuoteField]
-> IO (Maybe (Map (QuoteSymbol, QuoteField) QuoteValue))
So where you say optSymbol = getSymbol s you should just put the string s. The crucial line should certainly read
(ReqArg (\ s opts -> opts { optSymbol = s }) "SYMBOL")
not
(ReqArg (\ s opts -> opts { optSymbol = getSymbol s }) "SYMBOL")
At the moment you are just parsing arguments. What you do with the optSymbol string -- e.g. go to Yahoo to find out about it via getSymbol -- belongs in main or some preliminary action you need to define. Here is a module that typechecks, it barely does anything, acting like this:
-- $ ./yahoo -s STD.F -- Standard Chartered?
-- STD.F c -0.396 - -1.99%
-- STD.F l1 19.492
-- STD.F s STD.F
module Main where
import Finance.Quote.Yahoo
import Data.Time.Calendar
import Data.Map
import Prelude
import qualified Prelude
import System.Environment( getArgs )
import System.Console.GetOpt
import Data.Maybe ( fromMaybe )
import System.IO.Unsafe
main = do
args <- getArgs
(options, strs) <- compilerOpts args
whatIDoWithTheUsersOptions options strs
-- This is not doing much with all this user input ...
whatIDoWithTheUsersOptions :: Options -> [String] -> IO ()
whatIDoWithTheUsersOptions options strs = do
blather <- getSymbol $ optSymbol options
putStrLn blather
getSymbol :: String -> IO String
getSymbol arg = do
q <- getQuote [arg] ["s", "l1", "c"]
case q of
Nothing -> return $ "symbol " ++ arg ++ " not found"
Just m -> return $ unlines $ Prelude.map helper (toList m)
where helper ((a,b), c) = unwords [a,b,c]
data Options = Options
{ optVerbose :: Bool
, optShowVersion :: Bool
, optOutput :: Maybe FilePath
, optInput :: Maybe FilePath
, optLibDirs :: [FilePath]
, optSymbol :: String
} deriving Show
defaultOptions = Options
{ optVerbose = False
, optShowVersion = False
, optOutput = Nothing
, optInput = Nothing
, optLibDirs = []
, optSymbol = "YHOO"
}
options :: [OptDescr (Options -> Options)]
options =
[ Option ['v'] ["verbose"]
(NoArg (\ opts -> opts { optVerbose = True }))
"chatty output on stderr"
, Option ['V','?'] ["version"]
(NoArg (\ opts -> opts { optShowVersion = True }))
"show version number"
, Option ['o'] ["output"]
(OptArg ((\ f opts -> opts { optOutput = Just f }) . fromMaybe "output")
"FILE")
"output FILE"
, Option ['c'] []
(OptArg ((\ f opts -> opts { optInput = Just f }) . fromMaybe "input")
"FILE")
"input FILE"
, Option ['L'] ["libdir"]
(ReqArg (\ d opts -> opts { optLibDirs = optLibDirs opts ++ [d] }) "DIR")
"library directory"
, Option ['s'] ["symbol"]
(ReqArg (\ s opts -> opts { optSymbol = s }) "SYMBOL")
"symbol SYMBOL"
]
compilerOpts :: [String] -> IO (Options, [String])
compilerOpts argv =
case getOpt Permute options argv of
(o,n,[] ) -> return (Prelude.foldl (flip id) defaultOptions o, n)
(_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
where header = "Usage: ic [OPTION...] files..."
This is wrong:
getSymbol :: String -> String
getSymbol arg = do
q <- getQuote [arg] ["s", "l1", "c"]
case q of
Nothing -> error "symbol not found"
Just m -> m
It should probably be:
getSymbol :: String -> IO String
getSymbol arg = do
q <- getQuote [arg] ["s", "l1", "c"]
case q of
Nothing -> fail "symbol not found"
Just m -> return m
Note I have changed the type signature and the final two lines. (Since I have changed the type of getSymbol, you will have to use the result differently wherever you call it.)
A partial explanation:
You are using do syntax, which results in a monadic value. The return type of getSymbol was String, which is the same as [Char]. Lists are monads, so this is fine.
But then you call getQuote on the right of a <-, which results in an IO value. IO is monadic, but IO is not the same as lists, so we have an error. (You have to use the same monad on the right hand side of a <- as the do block results in.)
Edit: The above code change isn't going to be enough. m has type Map (QuoteSymbol, QuoteField) QuoteValue which obviously isn't the same as String, so you want to compute something from m instead of returning it directly. I don't know exactly what you want to do here.
Also, you're using getSymbol in your option parsing code. It's better if your save the input to getSymbol in your Options, and call getSymbol later with that value, after you have processed the command line options. (Otherwise you might contact Yahoo's servers only to discover that a different command line option is wrong and throw the answer away. Not to mention that the optSymbol field can't hold IO.)

Resources