类似于(>> =),但其中A函数返回一个不同单子

问题描述:

类型的(>>=)类似于(>> =),但其中A函数返回一个不同单子

(>>=) :: Monad m => m a -> (a -> m b) -> m b 

我想具有类型的函数:

(Monad m, Monad n) => m a -> (a -> n b) -> n b 

此函数可以是用于将不同的monad链接在一起。

我面临当我试图从命令行参数-p 30003000这个问题:

main = getArgs >>= (\args -> (elemIndex "-p" args) >>= (\id -> warpDebug (fromIntegral.read (args !! (id+1))) Ilm)) 

这显然不能编译,因为getArgs返回IO [String]elemIndex返回Maybe Int。上述类型的功能可以用来优雅地解决这个问题。我的问题是:

  • 此功能是否已定义? (Hoogle找不到)
  • 如果没有,可能是由于某种原因。那是什么原因?这被认为是不好的做法?我认为这是比使用案例表达更好的方法。

这样的函数不存在。事实上,如果你把n作为身份monad,它可以让你构建一个函数m a -> a,这个函数显然不能为所有monad定义。

为了解决“编写”两个monad的一般问题,你可以看看monad transformers

然而,在你的例子中使用monad变换器似乎有点矫枉过正。您可以简单地定义一个函数[String] -> Maybe Args(对于某些自定义类型Args-例如在示例中为Int),它执行命令行参数处理,然后从IO monad中对结果进行模式匹配(或使用maybe)。

这个函数不存在,因为它对所有monad都没有意义。它基本相当于一个monad拆包功能Monad m => m a -> a - 唯一的区别是你立即把它放入另一个monad中。

这个函数没有为所有monads定义的原因是因为它对其中的一些没有意义。例如,采取Maybe:解压缩的唯一方法是在出现Nothing时抛出错误,并且会查看运行时错误。一个更极端的例子是IO - 使用可能“解压”值的函数会导致奇怪的和潜在的不确定行为。

因此,你一般没有这样的功能。然而,许多具体的monads do带有这样的功能。一个很好的例子是runST;这实际上是一种处理国家的安全方式。您实际上具有MaybeIO(分别为fromJustunsafePerformIO)的这些功能,但他们有我上面概述的问题,你应该避免它们。

那么问题的解决方案就是查看是否存在你正在处理的单子的这种功能。如果有,检查任何潜在的陷阱 - 它是否会产生运行时错误或导致奇怪的行为?

在你的情况,如果你是绝对确保Maybe永远Nothing,使用fromJust。但是,这通常不是很好的做法,所以您应该坚持模式匹配Maybe以外的值。

答案取决于您是否需要一起使用或单独使用Maybe和IO monads。

如果您需要一起使用它们 - 答案是您需要构建一个包含IO monad和MaybeT monad变换器的monad变换器堆栈,以便构成IOMaybe monads。

如果单独需要他们,然后一个简单的解决方案的工作原理:

import System.Environment 
import Data.List 

main = getArgs >>= (\args -> return (elemIndex "-p" args 
    >>= \y -> return $ y + 900) >>= print) 

注意return。所以你在内部母体中有Maybe monad(在elemIndex900之间),但不是IO。也就是说,在离开Maybe monad之前,您无法执行IO操作,正如我在打印中显示的那样。