和Haskell一起玩,现在我尝试创建一个类似的函数
keepValue :: (Monad m) => m a -> (a -> m b) -> m a
Run Code Online (Sandbox Code Playgroud)
具有以下语义:它应该将monad值应用于函数,该函数返回第二个monad,并保留第一个monad 的结果,但是第二个monad的效果
我有Maybemonad 的工作函数:
keepValueMaybe :: Maybe a -> (a -> Maybe b) -> Maybe a
keepValue ma f = case ma >>= f of
Nothing -> Nothing
Just _ -> ma
Run Code Online (Sandbox Code Playgroud)
因此,如果第一个值是Nothing,则不运行该函数(因此没有第二个副作用),但如果第一个值是Just,则运行该函数(带副作用).我保持第二次计算的效果(例如,Nothing使整个表达式Nothing),但原始值.
现在我好奇.它适用于任何monad吗?
它看起来有点内置>>,但我在标准库中找不到任何东西.
Ant*_*sky 11
让我们来看看吧!
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = _
Run Code Online (Sandbox Code Playgroud)
那么我们想做keepValue什么?嗯,我们应该做的第一件事是使用ma,所以我们可以将它连接到f.
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
a <- ma
_
Run Code Online (Sandbox Code Playgroud)
现在我们有一个va类型的值a,所以我们可以传递给它f.
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
va <- ma
vb <- f va
_
Run Code Online (Sandbox Code Playgroud)
最后,我们想要制作va,所以我们可以这样做:
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
va <- ma
vb <- f va
return va
Run Code Online (Sandbox Code Playgroud)
这就是我如何编写像这样的任何monadic函数的初稿.然后,我会把它清理干净.首先,一些小东西:既然Applicative是一个超类Monad,我更喜欢pure到return; 我们没有用vb; 我会放弃v名字.所以对于do这个函数的基于注释的版本,我认为最好的选择是
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
a <- ma
_ <- f a
pure a
Run Code Online (Sandbox Code Playgroud)
但是,现在我们可以开始更好地实施.首先,我们可以_ <- f va用显式调用替换(>>):
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
a <- ma
f a >> pure a
Run Code Online (Sandbox Code Playgroud)
现在,我们可以应用简化.你可能知道,我们可随时更换(>>=)加pure/ return与fmap/ (<$>):任何的pure . f =<< ma,ma >>= pure . f或do a <- ma ; pure $ f a(所有这些是等同的)可以被替换f <$> ma.但是,Functor类型类还有另一种不太为人所知的方法(<$):
(<$) :: a -> f b -> f a
用相同的值替换输入中的所有位置.默认定义是fmap . const,但可以使用更高效的版本覆盖它.
所以我们有一个类似的替换规则(<$):我们可以随时替换ma >> pure b或do ma ; pure b使用b <$ ma.这给了我们
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
a <- ma
a <$ f a
Run Code Online (Sandbox Code Playgroud)
而且我认为这是此功能的最短合理版本!没有任何好的无点技巧可以让它变得更干净; 一个指标是a在do块的第二行多次使用.
顺便提一下,术语说明:你正在运行两个monadic动作,或两个monadic值 ; 你没有运行*"两个单子".monad就像Maybe- 一个支持(>>=)和的类型构造函数return.不要将这些值与类型混合在一起 - 这种术语上的区别有助于使事情更清晰!