如何检查一个IO()是否等效于另一个IO()?

时间:2019-03-06 09:22:11

标签: haskell

我正在使用hspec进行一些基本测试。

我有一个argsParser函数,该函数给出了一些参数,返回或更确切地说打印了它们的有效性。

argsParser :: [String] -> IO ()
argsParser args | null args = print "no args provided"
                | not $ null args && length args < 2 = print "no file name provided"
                | length args > 2 == print "too many arguments"
                | otherwise = goAhead args

问题是我不确定如何将IO ()与另一个IO ()进行比较。

我认为liftIO可能会有所帮助,但

x <- liftIO $ print "something"
y <- liftIO $ print "anything"

我明白了

x == y = True

我怀疑是因为两者都是actions

2 个答案:

答案 0 :(得分:12)

您无法将IO操作与其他操作进行比较。可计算性理论指出,无法确定两个IO值是否相等。因此,Haskell中没有实例Eq (IO a)

充其量,您可以尝试运行这两个动作,从外部观察它们的效果,然后比较它们的效果-这并不总是可行的(例如,如果一个动作是一个无限循环,则该动作需要用户输入)但它可能足够接近。可以通过将操作作为子流程运行,重定向其标准输出/错误来实现此检查。

(为什么要比较IO操作?这很不寻常)

答案 1 :(得分:3)

首先,将argsParser简化为 just 检查数字的内容 args;还没有做任何IO。

import System.IO

data ArgsError = NoArgs | NoFilename | TooManyArgs
instance Show ArgsError where
    show NoArgs = "no args specified"
    show NoFilename = "no file name specified"
    show TooManyArgs = "too many arguments"

validateArgs :: [String] -> Either ArgsError [String]
validateArgs args | null args = Left NoArgs
                  | length args < 2 = Left NoFilename
                  | length args > 2 = Left TooManyArgs
                  | otherwise = Right args

goAhead :: [String] -> IO ()
-- as before

现在,您只需为Either ArgsError [String]值提供一个 handler ,这是either :: (a -> c) -> (b -> c) -> Either a b -> c的简单应用。

main = do
    args <- getArgs
    either (hPrint stderr) goAhead (validatedArgs args)

您现在可以轻松测试validateArgs,并且可以说either (hPrint stderr) goAhead不需要 进行测试。