这对Haskell专业人员来说应该很容易..
我有一个可能值,
> let a = Just 5
Run Code Online (Sandbox Code Playgroud)
我可以打印出来:
> print a
Just 5
Run Code Online (Sandbox Code Playgroud)
但我想将一个I/O动作应用于Maybe的内部.我没有使用的唯一方法是找出如何做到这case
一点:
> maybe (return ()) print a
5
Run Code Online (Sandbox Code Playgroud)
但是,这似乎太冗长了.首先,return ()
特定于I/O monad,所以我必须为每个我想尝试这个技巧的monad提出一个不同的"零".
我想基本上将I/O操作(打印)映射到Maybe值,如果是Just
,则打印它,如果是,则不执行任何操作Nothing
.我想以某种方式表达它,
> fmap print a
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为print
是IO动作:
No instance for (Show (IO ()))
Run Code Online (Sandbox Code Playgroud)
我试过Applicative
,但无法弄清楚是否有办法表达它:
> print <$> a
No instance for (Show (IO ()))
Run Code Online (Sandbox Code Playgroud)
显然我对Monads-inside-monads有点困惑..谁能告诉我最简洁的表达方式的正确方法?
谢谢.
scl*_*clv 23
pelotom的答案是直截了当的.但不是有趣的!sequence
是Haskell函数,人们可以将其视为在列表和monad之间翻转类型构造函数的顺序.
sequence :: (Monad m) => [m a] -> m [a]
现在你想要的是,在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 ()
Run Code Online (Sandbox Code Playgroud)
请注意,还有一个sequenceA
稍微更通用的功能,不仅适用于Monads,还适用于所有Applicatives.
那么为什么要用这种方法?对于Maybe
明确区分它的方法很好.但是更大的数据结构Map
呢?例如?在这种情况下,traverse
,sequenceA
和朋友Data.Traversable
才能真正派上用场.
编辑:正如Ed'ka所说,traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
所以人们可以写traverse print $ Just 123
.
Tom*_*ett 16
首先,return()是特定于I/O monad的,所以我必须为每个我想尝试这个技巧的monad提出一个不同的"零".
return ()
实际上是非常通用的,从它的类型可以看出:
Prelude> :t return ()
return () :: (Monad m) => m ()
Run Code Online (Sandbox Code Playgroud)
我认为这种maybe (return ()) print a
做法没有错.