此实例似乎行为不正常:
> guard True <|> guard False
> guard False <|> guard False
*** Exception: user error (mzero)
有人可能会争辩说,这不会导致其他任何事情。但是为什么首先要定义这样的实例呢?每当评估没有意义时,是否有充分的理由导致
_|_
? 最佳答案
Alternative
的 IO
实例的目的是将可能失败(通过导致 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!"
无法执行第三个 Action 。由于
<|>
是左关联的并在其右侧之前尝试其左侧 Action ,因此执行此表达式的值将只执行 guard False <|> guard False
表示的任何成功 Action (例如,return ()
或其他),而永远不会尝试 putStrLn "success!"
。这里有一个微妙之处,可能会让你失望。与第一次出现相反,其值(value):
guard False <|> guard False
不是通常意义上的
_|_
。相反,它是一个完美定义的 IO 操作,如果执行该操作,就抛出异常而言将无法终止。不过,这种类型的非终止仍然有用,因为我们可以捕获它(例如,通过添加另一个 <|>
替代方法!)。另请注意,由于您没有提供更好的异常,因此会抛出
userError "mzero"
的默认异常。如果您通过以下方式导致失败:ioError (userError "one") <|> ioError (userError "two")
您会看到,如果所有操作都失败,则抛出的最后一个异常就是复合操作抛出的异常。
关于haskell - 实例替代 IO 的目的是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48450826/