提取嵌套的monadic结果:m(m a) - >嘛

时间:2014-11-25 18:46:23

标签: haskell monads functor applicative

我有一个功能

parseArgs :: [String] -> StdGen -> IO ()

选择要运行的函数。主要看起来像

main = parseArgs <$> getArgs <*> getStdGen >>= id

我遇到的问题parseArgs <$> getArgs <*> getStdGen的类型为IO (IO ()),我使用类型为(>>= id)的{​​{1}}提取。有没有办法避免在只有一行函数的情况下“提取”值?

3 个答案:

答案 0 :(得分:4)

最简单的方法是join

main = join $ parseArgs <$> getArgs <*> getStdGen

就个人而言,我更喜欢表格

main = join $ liftM2 parseArgs getArgs getStdGen

,其中

join   :: Monad m => m (m a) -> m a
liftM2 :: Monad m => (a -> b -> r) -> m a -> m b -> m r

或者只使用do

main = do
    args <- getArgs
    gen  <- getStdGen
    parseArgs args gen

答案 1 :(得分:3)

您可以为此定义运算符:

infixl 4 <&>

(<&>) :: Monad m => m (a -> m b) -> m a -> m b
f <&> x = f >>= (x >>=)

如果您有类型

的功能
f :: Monad m => (a1 -> a2 -> ... -> an -> m b) -> m a1 -> m a2 -> ... -> m an -> m b

然后你可以写

fx :: Monad m => m b
fx = f <$> x1 <*> x2 <*> ... <&> xn

其中每个xi的类型为m ai

在你的情况下,它只是

parseArgs <$> getArgs <&> getStdGen

答案 2 :(得分:2)

您可以将参数配对并将它们放在一个绑定中:

main = uncurry parseArgs =<< (,) <$> getArgs <*> getStdGen

这可以避免从嵌套IO中提取。不可否认,这并不短,但我觉得更容易思考。

它符合doTheWork =<< getAllTheInputs的一般模式,如果代码更复杂,这可能是你最终安排事情的方式。