Haskell Yahoo Finance Инструмент командной строки
Я пытаюсь использовать этот модуль для создания инструмента командной строки, который будет получать цитату из Yahoo Finance за символ. Когда я пытаюсь скомпилировать, я получаю эту ошибку.
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 } }
Я только начинаю изучать haskell - невероятно другой, но мощный язык, и я не могу понять, в чем проблема. Любая помощь будет принята с благодарностью, и я также буду признателен за любые отзывы о том, что я делаю это по принципу "haskell", и если нет, то никаких улучшений, которые я могу внести. Спасибо!
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
2 ответа
Беда в том, что getSymbol
has no business in the definition of options
, так как это IO
action as we can see from the fact that it uses getQuote
:
getQuote :: [QuoteSymbol]
-> [QuoteField]
-> IO (Maybe (Map (QuoteSymbol, QuoteField) QuoteValue))
Итак, где вы говорите optSymbol = getSymbol s
you should just put the string s. The crucial line should certainly read
(ReqArg (\ s opts -> opts { optSymbol = s }) "SYMBOL")
не
(ReqArg (\ s opts -> opts { optSymbol = getSymbol s }) "SYMBOL")
At the moment you are just parsing arguments. What you do with the optSymbol
string -- eg 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..."
Это не верно:
getSymbol :: String -> String
getSymbol arg = do
q <- getQuote [arg] ["s", "l1", "c"]
case q of
Nothing -> error "symbol not found"
Just m -> m
Вероятно, должно быть:
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
Обратите внимание, что я изменил тип подписи и последние две строки. (Так как я изменил тип getSymbol
вам придется использовать результат по-разному, где бы вы его ни называли.)
Частичное объяснение:
Ты используешь do
синтаксис, который приводит к монадическому значению. Тип возврата getSymbol
было String
, который так же, как [Char]
, Списки - это монады, так что это нормально.
Но тогда вы звоните getQuote
справа от <-
что приводит к IO
значение. IO
монадический, но IO
это не то же самое, что списки, поэтому у нас есть ошибка. (Вы должны использовать ту же монаду на правой стороне <-
как do
блокировать результаты в.)
Изменить: вышеуказанное изменение кода не будет достаточно. m
имеет тип Map (QuoteSymbol, QuoteField) QuoteValue
что, очевидно, не то же самое, что String
так что вы хотите вычислить что-то из m
вместо того, чтобы вернуть его напрямую. Я не знаю точно, что вы хотите сделать здесь.
Кроме того, вы используете getSymbol
в вашем варианте парсинг кода. Лучше, если вы сохраните вход в getSymbol
в вашем Options
и позвоните getSymbol
позже с этим значением, после обработки параметров командной строки. (В противном случае вы можете связаться с серверами Yahoo только для того, чтобы обнаружить, что другой параметр командной строки неверен, и выбросить ответ. Не говоря уже о том, что optSymbol
поле не может содержать IO
.)