【问题标题】:Conditional computation Maybe (IO ())条件计算 Maybe (IO ())
【发布时间】:2016-04-15 03:50:37
【问题描述】:

我对 Haskell 还很陌生,我想表达一下可能发生或不可能发生的 IO 操作的概念。

Network.URI 提供了解析 URI 的函数

parseURI :: String -> Maybe URI

在我的主要功能中,我得到用户的输入

main :: IO ()
main = do
  url <- getLine
  getResource url

以及使用parseURI函数

getResource :: String -> Maybe (IO ())
getResource url = parseURI url >>= doStuff

doStuff :: URI -> Maybe (IO ())
doStuff = undefined

如您所见,我想使用 &gt;&gt;= 来避免与 Maybes 打交道的 case of 级联。

我已经通过将 IO 嵌入到 Maybe 中来做到这一点。我想表达一个可能发生也可能不会发生的 IO 动作的情况。我应该使用什么?我肯定不是第一个遇到这个问题的人。我研究过 MaybeT 和 MonadIO,但我不确定这是我需要的。

具体来说,如果我将 doStuff 移至 MaybeT IO (),我仍然会使用 parseURI url :: Maybe String。如果我现在将其提升到 MaybeT,我将拥有 MaybeT Maybe String,这与我的 doStuff :: MaybeT IO () 不匹配

我希望它看起来更好(如果可能的话)并了解正确的方向以及使用的主流工具是什么。

感谢您的宝贵时间和建议。

【问题讨论】:

  • doStuff 可能返回Nothing 吗?如果没有,那么您可以改用fmap
  • 可能它可以,例如在做的事情中我使用uriAuthority :: Maybe URIAuth 来获取 URL 的域

标签: haskell


【解决方案1】:

首先:IO () 动作的类型可能会发生也可能不会发生,可以简单地是 IO ():你不关心结果,或者它是否成功,你只想让它执行 如果可能的话

如果您确实关心成功,那么有两种选择。

  • Maybe (IO ()) 是动作的类型还是不是动作,但是否是透明的“纯值”。即,如果您在顶层有一个 Maybe (IO ()) 值,那么您就可以确定一个动作是否可以发生而实际上不必首先执行任何 IO。但这不可能是您首先必须执行一些 IO 以确定关键操作是否会发生的值的类型,因为要绑定任何 IO,您已经必须位于 @ 的 Just 分支中987654327@!因此,如果成功取决于用户提供的字符串,那么这不是适合您的类型。
  • IO (Maybe ())IO 动作的类型,它可以做任何需要确定采取什么动作的事情。该类型实际上等价于 IO Bool - 一个返回一些指示标志的 IO 操作。您可以指定 Just () 表示已发生某些关键操作,Nothing 表示未发生,但无法从签名中实际看到这一点。

MaybeT IO () 相当于后者,实际上可能是最适合您的应用程序的类型。只是,您需要正确地进行变压器吊装! lift 从变压器堆栈中的向下 执行一个动作,直到堆栈的顶部。因此lift!例如,您需要它来将像 getLine :: IO String 这样的普通 IO 操作提升到 MaybeT IO 堆栈中:

lift getLine :: MaybeT IO String

但如果你有一个纯粹的Maybe 值,你宁愿需要“支撑”它以获得MaybeT IO。最简单的方法是直接使用MaybeT 构造函数。

getResource :: String -> MaybeT IO ()
getResource url = MaybeT (return $ parseURI url) >>= doStuff

doStuff :: URI -> MaybeT IO ()
doStuff = undefined

【讨论】:

  • 感谢您的解释,但我不确定我是否完全理解,所以这里是我的 cmets:wrt Maybe (IO ()) 你说:if you have a Maybe (IO ()) value on the top-level, then you're able to determine if an action can happen without actually having to do any IO first。 ...情况就是这样,因为我之前已经在做 IO 来获取用户输入。如果输入正确,我只想执行它,这就是我读取(可能是错误的)类型的方式:也许做点什么。 IO (Maybe ()) 类型对我来说是反直觉的,因为(对我来说)它意味着已经在做某事。
  • 我宁愿阅读Maybe (IO ()),因为也许能够做某事。你是对的,IO (Maybe ()) 暗示在做出决定之前已经可以做一些事情——正如你可能需要的那样!如果您事先分离了确定是否应采取操作所需的 IO,那么您根本不需要 MaybeIO 的任何组合:只需从 IO 检索必要的值并使用它用一个普通的条件(例如when)来决定是否执行另一个动作。
  • 另外,你提到了变压器堆栈。我猜堆栈是从左到右生长的,所以在变压器 Monad 的堆栈中更深,你的意思是最右边的类型?例如。在 MaybeT IO () 中,IO 将是堆栈中最深的。
  • 是的。在这种情况下,最右边 = 最内层 = 最下层(在语法树中)。
  • 感谢您的回答,我想我现在对很多事情有了更好的了解(lift 和 MabeyT 仅举几例)。你是个好老师。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-01
  • 1970-01-01
相关资源
最近更新 更多