3

我正在尝试使用此模块创建一个命令行工具,该工具将从 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
4

2 回答 2

1

这是错误的:

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语法,这会产生一元值。getSymbolwas的返回类型,String与 相同[Char]。列表是单子,所以这很好。

但是随后您调用getQuotea 的右侧<-,这会产生一个IO值。IO是一元的,但IO与列表不同,所以我们有一个错误。(您必须在 a 的右侧使用与块结果相同的 monad <-do

编辑:上面的代码更改是不够的。m具有Map (QuoteSymbol, QuoteField) QuoteValue显然与 不同的类型String,因此您希望从中计算某些m内容而不是直接返回它。我不知道你想在这里做什么。

此外,您正在使用getSymbol您的选项解析代码。最好将输入保存到getSymbol中,然后在处理完命令行选项后使用该值Options调用。getSymbol(否则你可能会联系雅虎的服务器,只是发现另一个命令行选项是错误的,然后把答案扔掉。更不用说该optSymbol字段不能保存IO。)

于 2012-03-21T08:18:11.297 回答
1

问题在于getSymbol的定义中没有业务options,因为它是一个IO动作,我们可以从它使用的事实中看出getQuote

getQuote :: [QuoteSymbol] 
         -> [QuoteField]   
         -> IO (Maybe (Map (QuoteSymbol, QuoteField) QuoteValue))

所以你说optSymbol = getSymbol s你应该把字符串s放在哪里。关键线当然应该阅读

(ReqArg (\ s opts -> opts { optSymbol =  s }) "SYMBOL")

不是

(ReqArg (\ s opts -> opts { optSymbol =  getSymbol s }) "SYMBOL")

目前你只是在解析参数。你对optSymbol字符串做什么——例如去Yahoo通过它来了解它getSymbol——属于main你需要定义的一些初步操作。这是一个类型检查的模块,它几乎不做任何事情,就像这样:

-- $ ./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..."
于 2012-03-21T19:09:10.097 回答