类似于(>> =)但返回不同monad的函数

时间:2011-12-23 09:05:37

标签: haskell monads

(>>=)的类型是

(>>=) :: 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。可以使用上述类型的功能来优雅地解决该问题。我的问题是:

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

3 个答案:

答案 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;这实际上是一种处理国家的安全方式。你实际上分别为MaybeIO(分别为fromJustunsafePerformIO)提供了这样的功能,但它们存在我上面列出的问题和你应该避免它们。

然后,问题的解决方案是查看是否存在这样一个函数,用于处理您正在处理的任何monad。如果有,请检查任何潜在的陷阱 - 它是否会产生运行时错误或导致奇怪的行为?

在您的情况下,如果您绝对确保Maybe永远不会Nothing,请使用fromJust。但是,这通常不是一种好习惯,因此您应该坚持使用Maybe中的值匹配模式。

答案 2 :(得分:2)

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

如果你需要一起使用它们 - 答案是你需要通过构造一堆包含IO monad和MaybeT monad变换器的monad变换器来组成IOMaybe monad。

如果你需要单独使用它们,那么一个更简单的解决方案就可以了:

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操作,正如我在print中所示。