有关monadic值的fmap有什么相似之处?

时间:2011-02-07 21:47:25

标签: haskell

这对Haskell专业人员来说应该很容易..

我有一个可能值,

> let a = Just 5

我可以打印出来:

> print a
Just 5

但我想将一个I / O动作应用于Maybe的内部。我在不使用case的情况下弄清楚如何执行此操作的唯一方法是:

> maybe (return ()) print a
5

然而,这似乎太冗长了。首先,return ()特定于I / O monad,所以我必须为每个我想尝试这个技巧的monad提出一个不同的“零”。

我想基本上将I / O操作(打印)映射到Maybe值,如果是Just则打印它,或者如果它是Nothing则不执行任何操作。我想以某种方式表达它,

> fmap print a

但是这不起作用,因为print是一个IO动作:

No instance for (Show (IO ()))

我试过了Applicative,但无法弄清楚是否有办法表达它:

> print <$> a
No instance for (Show (IO ()))

显然我对monads-inside-monads有点困惑..谁能告诉我最简洁的表达方式?

感谢。

4 个答案:

答案 0 :(得分:23)

pelotom的回答是直截了当的。但不是有趣的! sequence是Haskell函数,人们可以将其视为在列表和monad之间翻转类型构造函数的顺序。

sequence :: (Monad m) => [m a] -> m [a]

现在你想要的是,在Maybe和monad之间翻转类型构造函数的顺序。 Data.Traversable只输出一个sequence函数!

Data.Traversable.sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)

这可以像你的例子一样专注于Maybe (IO ()) -> IO (Maybe ())

因此:

Prelude Data.Traversable> Data.Traversable.sequence (fmap print $ Nothing)
Nothing

Prelude Data.Traversable> Data.Traversable.sequence (fmap print $ Just 123)
123
Just ()

请注意,还有一个sequenceA函数稍微更通用,不仅适用于Monads,还适用于所有Applicative。

那么为什么要使用这种方法呢?对于Maybe,明确区分的方法很好。但是更大的数据结构呢?例如Map?在这种情况下,traversesequenceA和来自Data.Traversable的朋友可以非常方便。

编辑:正如Ed'ka所说,traverse :: Applicative f => (a -> f b) -> t a -> f (t b)所以可以写traverse print $ Just 123

答案 1 :(得分:16)

  

首先,return()特定于I / O monad,所以我必须为每个我想尝试这个技巧的monad提出一个不同的“零”。

return ()实际上非常通用,可以从其类型中看出:

Prelude> :t return ()
return () :: (Monad m) => m ()

我认为maybe (return ()) print a方法没有错。

答案 2 :(得分:2)

  

但我想将一个I / O动作应用于Maybe。

的内部

这可以通过monad变换器来实现。

MaybeT是一个可以缠绕另一个monad的monad。换句话说,MaybeT可以使用任何其他Monad来抽象(无辜的[1])计算中的失败。

可悲的是,GHCi没有(2011年)有任何功能可以让你更容易玩monad变形金刚,但是你走了:

> :m + Control.Monad.Maybe Control.Monad.Trans
> let a = Just 5
> runMaybeT$ do { v <- MaybeT$ return a ; liftIO$ print v }
5
Just ()

为了更深入地了解monad和monad变换器,我建议你阅读网络上的其他资料。请记住,monads也只是包装值。

我会尽量保持简单。签名:m = IO,a =整数

runMaybeT :: MaybeT m a - &gt; m(可能是a) - 将MaybeT IO中的计算转换为IO中的计算。

执行 { - 在没有缩进的情况下使用符号使其适合ghci的提示[2]。

MaybeT :: m(也许是) - &gt; MaybeT m a - 包装类型IO的计算(可能是整数)。

返回 :: IO(Just Integer) - 将其替换为您的计算。

提升 - 在包装的monad中运行计算。[3]

Just() - 计算结果。只要不是(),GHCi就会打印IO结果。

MaybeT未包含在mtl中,因此您可能需要安装它

cabal install MaybeT

或考虑[1]


[1]也用于传递错误消息,请使用MonadError

[2]我知道GHCi中的多行输入

[3]如果你需要来自一堆monad的IO,请使用 liftIO

答案 3 :(得分:0)

你试过这个吗?

unwrap :: (Show a) => Maybe a -> IO ()
unwrap Nothing  = return ()
unwrap (Just a) = print a

在打开数据后,它将返回/打印提交的数据。