(>>=)
的类型是
(>>=) :: Monad m => m a -> (a -> m b) -> m b
我想要一个具有以下类型的函数:
(Monad m, Monad n) => m a -> (a -> n b) -> n b
此功能可用于将不同的monad链接在一起。
当我尝试从命令行参数3000
获取-p 3000
时,我遇到了这个问题:
main = getArgs >>= (\args -> (elemIndex "-p" args) >>= (\id -> warpDebug (fromIntegral.read (args !! (id+1))) Ilm))
这显然无法编译,因为getArgs
会返回IO [String]
而elemIndex
会返回Maybe Int
。可以使用上述类型的功能来优雅地解决该问题。我的问题是:
答案 0 :(得分:22)
这样的功能不存在。实际上,如果你将n
作为标识monad,它将允许你构造一个函数m a -> a
,这显然不能为所有monad定义。
要解决“编写”两个monad的一般问题,你可以查看monad transformers。
然而,在你的例子中使用monad变换器似乎有些过分。您可以简单地定义一个函数[String] -> Maybe Args
(对于某些自定义类型Args
- 例如示例中的Int
),它执行命令行参数处理,然后对结果进行模式匹配(或使用{ {3}})来自IO
monad。
答案 1 :(得分:7)
此功能不存在,因为它对所有monad都没有意义。它基本上相当于monad解包函数Monad m => m a -> a
- 唯一的区别是你立即将它放入另一个monad中。
没有为所有monad定义此函数的原因是因为它对其中一些没有意义。例如,取Maybe
:解压缩它的唯一方法是在Nothing
时抛出错误,并且忽略运行时错误。一个更极端的例子是IO
- 使用一个可以“解包”IO
值的函数会导致奇怪的,可能是不确定的行为。
因此,您通常没有这样的功能。但是,很多特定的monad 做都带有这样的功能。一个很好的例子是runST
;这实际上是一种处理国家的安全方式。你实际上做分别为Maybe
和IO
(分别为fromJust
和unsafePerformIO
)提供了这样的功能,但它们存在我上面列出的问题和你应该避免它们。
然后,问题的解决方案是查看是否存在这样一个函数,用于处理您正在处理的任何monad。如果有,请检查任何潜在的陷阱 - 它是否会产生运行时错误或导致奇怪的行为?
在您的情况下,如果您绝对确保Maybe
永远不会Nothing
,请使用fromJust
。但是,这通常不是一种好习惯,因此您应该坚持使用Maybe
中的值匹配模式。
答案 2 :(得分:2)
答案取决于您是否需要一起或单独使用Maybe和IO monad。
如果你需要一起使用它们 - 答案是你需要通过构造一堆包含IO monad和MaybeT monad变换器的monad变换器来组成IO
和Maybe
monad。
如果你需要单独使用它们,那么一个更简单的解决方案就可以了:
import System.Environment
import Data.List
main = getArgs >>= (\args -> return (elemIndex "-p" args
>>= \y -> return $ y + 900) >>= print)
请注意return
。所以你在内部括号中有Maybe
monad(在elemIndex
和900
之间),但不是IO。也就是说,在离开Maybe
monad之前,你无法执行IO操作,正如我在print中所示。