【问题标题】:MonadPlus definition for Haskell IOHaskell IO 的 MonadPlus 定义
【发布时间】:2011-05-29 02:48:22
【问题描述】:

我只是写了一点代码,我想在 IO Monad 中使用guard function。但是,有no definition of MonadPlus for IO 这意味着我们不能在 IO 域中使用守卫。我见过an example of using the MabyeT transformer to use guard in the Maybe Monad,然后解除了所有的 IO 操作,但如果我不需要,我真的不想这样做。

我想要的一些例子可能是:

handleFlags :: [Flag] -> IO ()
handleFlags flags = do
    when (Help `elem` flags) (putStrLn "Usage: program_name options...")
    guard (Help `elem` flags)
    ... do stuff ...
    return ()

我想知道是否有一种很好的方法可以通过 MonadPlus 的声明或其他方式在 IO Monad 中获取保护函数(或类似的东西)。或者我做错了;有没有更好的方法在上面的函数中编写帮助消息?谢谢。

(PS 我可以使用 if-then-else 语句,但它似乎以某种方式破坏了这一点。更不用说对于很多选项它会导致大量嵌套。)

【问题讨论】:

标签: haskell io monads alternative-functor monadplus


【解决方案1】:

为此专门设计了一些函数:在 Control.Monad 中,函数 when 和它的对应函数 unless。 安东尼的回答可以改写成这样:

handleFlags :: [Flag] -> IO ()
handleFlags flags =
    when (Help `elem` flags) $ putStrLn "Usage: program_name options..."

规格:

when :: (Applicative m) => Bool -> m () -> m ()
unless bool = when (not bool)

Link to docs on hackage.haskell.org

如果需要更多,这里是link to another package, specifically monad-oriented and with several more utilities: Control.Monad.IfElse

【讨论】:

    【解决方案2】:

    考虑MonadPlus的定义:

    class Monad m => MonadPlus m where
        mzero :: m a 
        mplus :: m a -> m a -> m a
    

    您将如何为IO 实现mzeroIO a 类型的值表示返回 a 类型的 IO 计算,因此 mzero 必须是返回任何可能类型的 IO 计算。显然,无法为某个任意类型生成一个值,而且与 Maybe 不同的是,我们没有可以使用的“空”构造函数,因此 mzero必然代表一个永远不会返回的 IO 计算 .

    如何编写一个永不返回的 IO 计算?基本上,要么进入无限循环,要么抛出运行时错误。前者的效用令人怀疑,所以后者是你所坚持的。

    简而言之,要为IO 编写一个MonadPlus 的实例,你要做的是:让mzero 抛出一个运行时异常,并让mplus 评估它的第一个参数,同时捕获由mzero。如果没有引发异常,则返回结果。如果引发异常,请评估 mplus 的第二个参数,同时忽略异常。

    也就是说,运行时异常通常被认为是不可取的,所以在走这条路之前我会犹豫不决。如果您确实想这样做(并且不介意增加程序在运行时崩溃的可能性),您将在Control.Exception 中找到实现上述内容所需的一切。

    在实践中,如果我想要大量 guarding 来评估 monadic 表达式的结果,或者如果大多数条件依赖于作为函数参数提供的纯值(您示例中的标志是)使用@Anthony 回答中的模式防护。

    【讨论】:

    • 这是一个很好的答案,它让我明白了为什么 MonadPlus for IO 在没有错误或无限循环的情况下真的毫无意义。
    • @Robert Massaioli:这并不是说这有什么错误。这是一个完全正确的MonadPlus 实例,它等同于MonadPlus 如何使用Either e 作为错误单子,我认为IO 的这样一个实例甚至存在于一个单子转换器库中。唯一的问题是你必须更加小心地捕捉错误,因为与MaybeT 或类似的不同,异常可以一直逃到main
    【解决方案3】:

    我用守卫做这种事情。

    handleFlags :: [Flag] -> IO ()
    handleFlags flags
      | Help `elem` flags = putStrLn "Usage: program_name options..."
      | otherwise = return ()
    

    【讨论】:

    • 有时您知道自己走在正确的轨道上(使用警卫),但随后有人指出了正确的想法(this),您的额头就会被打耳光。谢谢,我用过这个,这正是我想要的。虽然我将 camccann 标记为答案,因为他说明了为什么我的想法是一个糟糕的想法,我的替代方案是什么,然后指出你的替代方案是他会做的。
    • 请注意,有像 cmdargs 这样的库可以为您处理这些东西 :)
    • fwiw,我喜欢 cmdargs 的魔力太多了。我更喜欢 parseargs,因为这一切都是光明正大的。
    • 我更喜欢这种方法,特别是如果您必须检查许多选项,但如果您只有一个选项,那么 Control.Monad.when 可能会有所帮助。 (我知道这是一个旧线程,只是以防某人找到它,就像我一样)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-28
    • 1970-01-01
    • 2019-04-01
    • 2020-02-03
    • 2012-03-28
    相关资源
    最近更新 更多