Haskell Yahoo Finance Command Line Tool
I am trying to use this module to create a command line tool that will grab a yahoo finance quote for a symbol. When I try to compile, I get 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 yet powerful language and I cannot figure out what the problem is. Any help would be much appreciated and I would also appreciate any feedback if I make this "haskell" and if not any improvements I can make. Thank!
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
source to share
The problem is that getSymbol
it has no business in the definition options
as it is an action IO
as we can see from what 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 critical line must be read
(ReqArg (\ s opts -> opts { optSymbol = s }) "SYMBOL")
not
(ReqArg (\ s opts -> opts { optSymbol = getSymbol s }) "SYMBOL")
For now, you're just parsing the arguments. What you do with a string optSymbol
- for example, go to Yahoo to inquire about it through getSymbol
- belongs to main
or some preliminary action that you need to define. Here is the module that typechecks, it does almost nothing, 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..."
source to share
It is not right:
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 changed the type signature and the last two lines. (Since I changed the type getSymbol
, you will have to use the result differently wherever you name it.)
Partial explanation:
You are using a syntax do
that results in a monadic value. The return type getSymbol
was String
, which is the same as [Char]
. Lists are monads, so that's okay.
But then you call to the getQuote
right of <-
, which results in the value IO
. IO
is monadic, but IO
not the same as lists, so we have an error. (You must use the same monad on the right side <-
when the block is executed as a result do
.)
Edit: The above code change would not be sufficient. m
has a type Map (QuoteSymbol, QuoteField) QuoteValue
that is obviously not 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 are using getSymbol
in your syntax code. It's better if you save the input getSymbol
to Options
and call getSymbol
later with this value after you've processed the command line parameters. (Otherwise, you might contact Yahoo's servers only to find that another command line option is wrong and discard the response. Not to mention, the field optSymbol
cannot contain IO
.)
source to share