有条件的计算 Maybe (IO ())

3

我对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

正如您所看到的,我想使用>>=来避免处理Maybes时出现级联情况。我已经通过将IO嵌入到Maybe中实现了这一点。我想表达的是可能发生也可能不发生IO操作的情况。我应该使用什么?我肯定不是第一个遇到这个问题的人。我研究过MaybeT和MonadIO,但我不确定那是否是我需要的。

具体来说,如果我将doStuff移动到MaybeT IO (),我仍然会被parseURI url :: Maybe String困住。如果我将其提升到MaybeT,现在我有了MaybeT Maybe String,这将无法匹配我的doStuff :: MaybeT IO ()

如果可能的话,我希望这看起来更好,并且理解正确的方向和使用的主流工具。

感谢您的时间和建议。


doStuff 可能会返回 Nothing 吗?如果不会,那么你可以使用 fmap - Aadit M Shah
潜在地,例如在我使用 uriAuthority :: Maybe URIAuth 来获取URL的域名时,它可以做一些事情。 - tmpz
1
请参阅如何处理多层缩进? - Daniel Wagner
1个回答

3
首先:可能发生或可能不发生的 IO () 操作的类型可以简单地是 IO ():您不关心结果或它是否成功,您只希望在可能的情况下执行它。
如果您关心成功,则有两个选项。
- Maybe (IO ()) 是动作或非动作的类型,但是否为一个透明的“纯值”。即,如果您在顶层有一个 Maybe (IO ()) 值,则能够确定是否可以执行某个操作,而无需实际进行任何 IO。但这不能是您必须先执行一些 IO 才能确定关键操作是否将发生的值的类型,因为要绑定任何 IO,您已经必须处于 Just 分支中!因此,如果成功取决于用户提供的字符串,则此类型对您来说不正确。 - IO (Maybe ()) 是一个 IO 操作的类型,它可以执行任何需要确定要执行的操作的操作。该类型实际上等同于 IO Bool - 返回一些指示标志的 IO 操作。您可以指定 Just () 表示已发生某些关键操作,而 Nothing 表示未发生,但从签名中没有办法看到这一点。 MaybeT IO () 等同于后者,并且实际上可能是您的应用程序最佳类型。只是,您需要正确地进行转换器提升!lift 将动作从变换器堆栈中向上提升到该堆栈的顶部。因此,您需要它,例如将普通的 IO 操作(如 getLine :: IO String)提升到 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

谢谢您的解释,但我不确定我完全理解了,所以这是我的评论:关于Maybe (IO ()),您说:“如果您在顶层有一个Maybe(IO())值,则可以确定是否可以执行操作,而无需先执行任何IO。”...因为我已经在之前进行了IO以获取用户输入。我只想在输入正确时执行它,这就是我(可能错误地)读取类型的方式:Maybe do something。对我来说,类型IO(Maybe())的阅读方式与直觉相反,因为它似乎已经做了某些事情。 - tmpz
我更愿意将 Maybe (IO ()) 理解为 _可能能够做某事_。你说得对,IO (Maybe ()) 意味着在决策之前已经可以做一些事情 - 正如你可能需要的那样!如果你事先分离了用于确定是否应该执行操作的 IO,则根本不需要任何 MaybeIO 的组合:只需从 IO 中检索所需的值,并使用它来决定是否使用普通条件(例如 when)执行其他操作。 - leftaroundabout
另外,你提到了Transformer Stack。我猜测这个Stack是从左到右增长的,所以当你说一个Transformer Monad在Stack中越深时,你指的是最右边的类型?例如,在MaybeT IO()中,IO将会是Stack中最深的部分。 - tmpz
是的,在这种情况下,最右边 = 最内层 = 最底层(在语法树中)。 - leftaroundabout
感谢您的答案,我现在对很多事情有了更好的理解(例如Lift和MabeyT)。你是一位出色的教师。 - tmpz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接