所以,我尝试做的是在使用之前打印两个值。这里有两种完美的工作方式,但我觉得还有其他方法可以做到。我知道这样做的建议方法很可能就是阻止,因为它的布局是最简单的,但为什么不看其他呢?
-- 1
main = do
a <- m1
b <- m2
print a
print b
print $ f a b
-- 2
p x = print x >> return x
main = print =<< liftM2 f (p =<< m1) (p =<< m2)
我也试过这些,但是他们遇到了问题,我们无法弄明白,而且似乎没有合适的东西连在一起。
main = print =<< foldl1 f . mapM p =<< sequence [m1, m2]
main = print =<< on f p m1 m2
main = print . curry (on f) =<< (uncurry (join (***)) p) m1 m2
我认为来自Data.Function的on
非常接近我想做的事情,但我不知道如何管理两个monadic值。提前感谢任何建议。
编辑:第一个问题有点宽泛:如何重写第一个do块?我的另一个问题是如何从monad中提取两个值到一个带两个值的函数?
如果某个函数执行(m a, m a) -> m (a, a)
之类的操作,那么我认为可以curry (on f p) =<< thatfunction (m1, m2)
,但可能有另一种方式。
答案 0 :(得分:1)
如果你想让事情变得更短,你可以创建一些执行打印的Applicative式组合器:
import Control.Monad.IO.Class (MonadIO(..))
(<*&>) :: (MonadIO m, Show a) => m (a -> b) -> m a -> m b
mf <*&> mx = do
f <- mf
x <- mx
liftIO $ print x
return $ f x
(<$&>) :: (MonadIO m, Show a) => (a -> b) -> m a -> m b
f <$&> mx = pure f <*&> mx
(名字可能会留下一些不足之处,但我会把它留给你。)
使用这些运算符,您可以像这样编写原始示例:
main = print $ f <$&> m1 <*&> m2
您是否认为这更清楚取决于您(可能取决于您使用此模式的频率)。
答案 1 :(得分:0)
你可能想要这个:
main = (liftM2 f `on` (>>= p)) m1 m2 >>= print
where f = (++)
p x = print x >> return x
m1 = getLine
m2 = getLine
liftM2 f
创建f
版本,其“参数”均为monadic值。on
创建一个函数,将“参数”传递到(>>= p)
,然后再传递给liftM2 f
。print
。结果,这个例子