haskell - 单值的 fmap 有什么相似之处?

标签 haskell

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

我有一个 Maybe 值,

> let a = Just 5

我可以打印它:
> print a
Just 5

但我想在 Maybe 内部应用一个 I/O Action 。我想出如何在不使用 case 的情况下做到这一点的唯一方法是:
> maybe (return ()) print a
5

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

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

但这不起作用,因为 print是一个 IO Action :
No instance for (Show (IO ()))

我试过Applicative ,但不知道是否有办法表达它:
> print <$> a
No instance for (Show (IO ()))

显然,我对 monads-inside-monads 有点困惑。谁能告诉我最简洁地表达这一点的正确方法?

谢谢。

最佳答案

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,而且适用于所有 Applicatives。

那么为什么要使用这种方法呢?对于 Maybe明确地将其分开的方法很好。但是更大的数据结构呢——Map例如?在这种情况下,traverse , sequenceA和来自 Data.Traversable 的 friend 可以很方便。

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

关于haskell - 单值的 fmap 有什么相似之处?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4927014/

相关文章:

windows - 如何将进程的 stdout 和 stderr 通过管道传输到同一个句柄?

haskell - 为什么此代码返回 Nothing 而不是抛出异常

haskell - 高阶函数,输入 `|' 时解析错误

haskell - Haskell 中的舍入到最近函数

函数应用: Why is $ used here?

haskell - MonadState 类型类声明的语法

docker - 如何定义抖动规则来构建 docker 镜像?

haskell - 在 Haskell 中对 3 个参数进行柯里化(Currying)

haskell - 为什么我不能使用 seq 强制执行 IO 操作?

haskell - 为什么 Haskell 中没有 runConst 函数?