实例替代IO的目的是什么? [英] What is the purpose of instance Alternative IO?

查看:74
本文介绍了实例替代IO的目的是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此实例的行为似乎不正确:

> guard True <|> guard False

> guard False <|> guard False
*** Exception: user error (mzero)

有人可能会辩称,这不会导致其他任何结果.但是为什么要首先定义这样的实例呢?每当评估没有意义时,是否有充分的理由导致_|_?

解决方案

IOAlternative实例的目的是组合可能失败的IO操作(通过导致IO错误或引发异常) )添加到一个单独的IO动作中,该动作依次尝试"多个动作,接受第一个成功的动作,或者-如果所有动作均失败,则该动作本身会失败.

因此,类似这样的方法可以从标准输入中读取一个或多个行(使用some),否则(使用<|>)抱怨如果没有可用的行:

main = (print =<< some getLine) <|> putStrLn "No input!"

或者您可以编写如下内容:

readConfig :: IO Config
readConfig = readConfigFile "~/.local/myapp/config"
         <|> readConfigFile "/etc/myapp/config"
         <|> return defaultConfig

鉴于此,完全有道理:

guard False <|> guard False

表示一个动作,该动作在执行时必须通过生成异常而失败.如果没有,如@danidaz所指出的,则执行操作:

guard False <|> guard False <|> putStrLn "success!"

无法执行第三个动作.由于<|>是左关联的,并且在其右侧之前尝试其左操作,因此执行该表达式的值将只执行guard False <|> guard False表示的任何成功操作(例如,return ()或其他任何操作),而从不尝试putStrLn "success!".

这里有些微妙之处可能会让您失望.与首次出现相反,其值:

guard False <|> guard False

不是通常意义上的_|_.相反,这是一个定义完善的IO操作,如果被执行,将不会在引发异常的意义上终止.但是,这种类型的非终止仍然有用,因为我们可以捕获它(例如,添加另一个<|>替代方法!).

还要注意,因为您没有提供更好的异常,所以会抛出默认异常userError "mzero".如果您是通过以下方式导致失败的话:

ioError (userError "one") <|> ioError (userError "two")

您会看到,如果所有操作均失败,则抛出的最后一个异常是复合操作抛出的异常.

This instance doesn't seem to behave properly:

> guard True <|> guard False

> guard False <|> guard False
*** Exception: user error (mzero)

One might argue that this cannot result in anything else. But why define such instance in the first place? Is there any good reason to result in _|_ whenever evaluation does not make sense?

解决方案

The purpose of the Alternative instance for IO is to combine IO actions that might fail (by causing an IO error or otherwise throwing an exception) into a single IO action that "tries" multiple actions in turn, accepting the first successful one, or -- if all actions fail -- fails itself.

So, something like this would work to read one or more lines (using some) from standard input or else (using <|>) complain if no lines are available:

main = (print =<< some getLine) <|> putStrLn "No input!"

or you could write something like:

readConfig :: IO Config
readConfig = readConfigFile "~/.local/myapp/config"
         <|> readConfigFile "/etc/myapp/config"
         <|> return defaultConfig

Given this, it makes perfect sense that:

guard False <|> guard False

represents an action that, when executed, must fail by generating an exception. If it didn't, as @danidaz has pointed out, then executing the action:

guard False <|> guard False <|> putStrLn "success!"

wouldn't work to execute the third action. Since <|> is left associative and tries its left action before its right, executing the value of this expression would just execute whatever successful action guard False <|> guard False represented (e.g., return () or whatever) and never try putStrLn "success!".

There's a subtlety here that may be throwing you off. Contrary to first appearances, the value of:

guard False <|> guard False

isn't _|_ in the usual sense. Rather it's a perfectly well defined IO action that, if executed will fail to terminate in the sense of throwing an exception. That type of non-termination is still useful, though, because we can catch it (by adding another <|> alternative, for example!).

Also note, because you haven't supplied a better exception, a default exception of userError "mzero" is thrown. If you had instead caused failure via:

ioError (userError "one") <|> ioError (userError "two")

you'd see that if all actions fail, the last exception thrown is the one that gets thrown by the composite action.

这篇关于实例替代IO的目的是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆